Merge branch 'feat/nfc-type-4-final' into mntm-dev

This commit is contained in:
WillyJL
2025-10-05 23:24:30 +02:00
242 changed files with 10348 additions and 697 deletions

View File

@@ -1,5 +1,7 @@
REM This is BadUSB demo script for windows
REM set slightly slower delay to ensure notepad picks up input without skipping characters
DEFAULT_STRING_DELAY 10
REM Open windows notepad
DELAY 1000
GUI r

View File

@@ -13,7 +13,7 @@
#define INFRARED_LIBRARY_HEADER "IR library file"
#define INFRARED_LIBRARY_VERSION (1)
ARRAY_DEF(SignalPositionArray, size_t, M_DEFAULT_OPLIST);
ARRAY_DEF(SignalPositionArray, size_t, M_DEFAULT_OPLIST); //-V658
typedef struct {
size_t index;

View File

@@ -2118,3 +2118,79 @@ frequency: 38000
duty_cycle: 0.330000
data: 39670 99106 3227 1570 424 406 422 407 422 1183 424 405 424 1184 448 380 423 407 421 406 424 1183 424 1185 421 406 423 404 424 405 424 1184 423 1182 425 407 448 380 423 407 422 405 423 406 422 406 424 405 423 407 421 406 423 407 422 405 424 405 423 406 423 1184 422 408 421 408 422 405 424 406 421 407 422 406 423 405 423 1183 424 406 423 405 423 405 423 405 423 1186 421 1184 422 1184 422 1185 422 1184 447 1159 423 1184 422 1184 422 408 421 407 423 1184 421 407 448 381 422 405 423 409 421 406 422 406 422 407 422 406 423 1183 423 1185 422 406 423 405 424 1184 423 408 421 405 424 405 424 1184 422 1185 422 1184 422 407 423 408 420 409 420 1185 447 382 423 405 423 408 421 406 423 407 422 406 423 406 423 408 421 406 423 1183 424 407 422 406 424 405 424 406 423 407 423 406 423 408 422 407 422 405 424 408 421 407 422 407 422 406 423 406 423 407 422 406 423 406 422 408 421 407 422 408 421 407 422 406 423 408 422 406 423 405 423 409 422 406 422 406 423 406 423 407 422 407 423 405 424 1184 423 407 421 406 424 1184 423 1184 422 407 423 1183 423 405 424 1184 423 409 420 407 422
#
# Model: Daikin FTXN25LV1B9
#
name: Off
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9876 9703 9956 9681 4738 2366 489 310 423 822 488 822 488 311 422 823 487 311 423 310 424 310 423 310 424 823 487 311 422 310 424 823 487 310 424 311 422 310 424 311 422 823 487 311 422 310 423 310 424 823 488 311 423 310 423 310 423 823 488 310 423 310 424 310 424 823 487 310 424 311 423 824 487 823 487 823 487 310 423 823 487 311 423 311 423 310 424 311 422 824 487 310 423 311 422 824 487 311 422 310 424 310 423 824 486 824 487 310 424 310 423 311 423 824 487 311 422 310 423 310 424 310 423 824 486 824 486 310 424 824 486 824 486 824 486 20121 4685
#
name: Dh
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9907 9648 9958 9677 4742 2362 492 311 422 820 490 820 435 312 477 821 434 312 421 312 422 312 422 876 434 312 422 312 422 312 422 876 434 312 422 312 422 312 421 312 422 876 434 312 422 312 421 312 422 876 434 312 422 311 422 312 477 821 434 312 422 312 421 312 477 821 434 312 422 312 422 876 434 877 434 876 434 312 422 876 434 312 422 311 422 312 422 312 422 876 434 312 422 312 422 876 434 312 422 312 421 312 422 877 433 876 434 312 422 312 422 312 422 876 434 312 422 312 421 312 422 312 422 877 433 312 422 877 433 312 422 877 433 312 422 20174 4740
#
name: Heat_lo
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9827 9757 9876 9732 4687 2418 435 311 422 876 434 876 434 311 423 877 433 312 421 311 423 311 422 311 423 311 423 311 423 877 433 877 433 311 423 311 423 312 422 877 433 877 433 311 423 311 423 311 423 877 434 311 422 311 423 311 422 877 433 311 423 312 421 312 422 877 433 311 423 312 421 877 433 877 433 877 434 311 423 877 433 311 423 311 423 311 422 311 423 877 433 312 422 311 423 877 433 311 423 312 422 311 423 877 433 878 433 311 423 312 422 312 422 877 433 311 423 311 422 311 423 312 422 878 432 312 422 877 433 311 423 877 433 878 433 20174 4685
#
name: Heat_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9898 9680 9901 9734 4685 2420 434 312 421 876 434 876 434 312 422 876 434 312 422 312 422 312 422 312 421 312 422 312 422 877 433 877 433 312 422 312 421 312 422 877 433 877 433 312 422 312 422 312 422 876 434 312 422 312 422 312 422 877 434 312 422 312 421 312 422 877 433 312 421 312 422 877 433 877 434 877 433 312 422 877 433 312 422 312 421 312 422 312 422 877 433 312 421 312 422 877 433 312 422 312 422 312 421 312 422 312 422 312 422 312 421 877 433 877 434 312 421 312 422 312 422 312 422 877 433 312 422 877 433 877 433 312 421 877 433 20174 4684
#
name: Cool_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9932 9649 9959 9677 4742 2362 492 311 422 819 491 819 491 311 422 820 490 311 423 311 423 311 423 311 422 820 490 310 423 310 424 820 490 311 423 311 423 310 424 820 490 311 422 820 491 311 423 311 422 820 491 311 422 311 423 311 423 820 490 311 423 311 423 311 423 820 490 310 424 311 423 820 490 820 490 820 490 310 424 820 490 311 423 310 424 310 424 311 422 820 491 311 422 310 424 820 490 310 424 311 423 310 423 311 423 820 490 820 490 311 423 820 490 311 422 311 423 311 423 311 423 311 422 821 490 311 422 821 490 820 490 311 422 821 490 20118 4740
#
name: Cool_lo
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9878 9704 9903 9733 4687 2418 435 311 422 875 435 875 435 311 423 875 435 311 423 311 422 311 423 311 423 875 435 311 423 311 423 875 435 311 423 311 423 311 422 876 434 311 423 876 434 311 423 311 423 876 434 311 423 311 423 311 423 876 434 311 423 311 423 311 423 875 435 311 422 311 423 876 434 876 435 876 434 311 423 876 434 311 423 311 423 311 422 311 423 876 434 311 422 311 423 876 434 311 423 311 422 311 423 876 434 876 434 311 423 311 422 311 423 876 435 311 422 311 422 311 423 311 423 876 434 311 423 876 434 311 423 311 423 876 434 20174 4685
#
# Model: Toyotomi KTN22-12R32
#
name: Off
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9067 4414 724 1584 753 452 754 453 753 478 728 478 727 479 726 480 726 481 725 1584 725 1585 724 1584 725 482 724 482 725 482 725 482 724 483 724 483 723 482 725 483 723 483 723 482 724 1585 724 482 724 482 724 483 724 482 725 483 723 483 723 1584 724 482 724 1585 723 482 724 483 723 1585 723 483 723 19925 724 483 724 1585 724 482 724 482 724 482 724 482 724 483 724 482 724 482 724 1585 724 483 724 483 724 482 725 483 724 1585 723 1585 724 483 723 483 724 483 724 483 724 482 723 483 724 482 724 482 724 483 723 483 723 483 724 483 723 483 724 1585 724 1585 723 1585 724 39949 9071 4415 725 482 725 481 725 482 725 483 723 481 725 482 724 483 724 482 724 482 724 482 724 482 724 482 725 483 723 483 723 482 725 483 722 483 724 483 723 482 724 483 724 483 723 482 724 483 723 483 724 483 723 483 723 483 723 483 724 482 724 1585 723 483 724 1585 725 482 724 1585 724 483 724 19926 724 483 723 482 725 482 724 482 724 482 725 482 724 482 725 483 724 482 723 482 725 483 723 482 725 482 725 483 723 483 724 483 723 483 724 483 723 482 724 483 723 483 724 482 725 482 724 483 724 483 723 482 724 483 723 483 723 482 725 1585 723 483 724 1585 724
#
name: Dh
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9042 4416 751 454 753 1555 753 453 754 1556 753 1558 750 479 726 480 726 482 724 1584 725 481 724 482 725 1585 725 482 725 482 724 482 724 482 725 482 725 483 723 483 724 482 724 482 724 1586 723 1585 723 482 724 483 724 483 724 483 724 482 725 1585 724 482 724 1585 724 484 723 482 724 1585 723 483 724 19928 723 482 725 482 724 482 725 483 724 483 724 482 725 482 724 483 724 483 723 1585 723 482 724 483 723 482 724 483 724 1585 724 1585 724 482 724 482 724 482 725 482 725 482 724 483 724 483 723 483 724 482 724 483 724 483 724 483 725 1585 724 483 723 482 724 1585 723 39946 9070 4414 725 481 725 481 725 482 724 482 724 482 724 481 725 482 724 482 724 482 725 483 723 482 725 483 724 482 725 482 724 482 724 482 724 483 723 483 724 483 724 482 725 483 724 482 724 482 725 483 723 482 725 483 723 482 724 483 724 482 724 1584 724 482 724 1585 724 483 724 1586 723 482 725 19926 724 482 724 482 724 482 725 483 723 482 725 482 725 482 724 483 724 482 724 482 725 483 724 482 725 483 723 483 723 483 723 482 724 483 724 483 723 482 724 483 724 482 724 483 723 483 723 483 724 483 723 483 723 482 724 483 723 483 724 1585 724 483 723 1586 723
#
name: Heat_lo
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9040 4431 702 503 702 503 702 1604 758 1548 756 448 729 475 730 475 729 477 728 1579 726 1579 727 1580 726 479 726 481 724 480 724 481 724 482 724 482 723 482 723 482 723 482 723 483 722 1583 724 1584 722 483 723 483 722 482 723 483 722 483 723 1584 722 482 724 1584 722 482 723 483 722 1585 721 483 723 19907 724 481 723 482 723 482 723 482 723 482 723 482 723 483 723 483 722 482 723 1584 722 482 722 482 723 483 723 482 723 1584 723 1584 721 483 722 483 722 483 722 484 721 482 723 483 723 483 722 483 722 483 723 483 722 484 721 484 721 1607 699 483 722 483 722 1608 698 39905 9065 4406 726 479 726 480 724 480 725 481 725 481 724 482 723 482 723 482 724 482 723 483 723 482 723 482 723 482 723 482 724 483 722 482 723 483 723 483 723 483 722 483 723 483 722 483 722 483 722 507 699 482 723 484 722 483 722 483 722 483 722 1607 699 483 722 1585 722 483 722 1584 723 506 698 19908 723 481 724 482 723 482 724 482 722 482 724 482 724 482 723 483 722 482 723 483 722 482 723 483 722 483 722 483 722 482 723 506 699 483 722 484 722 482 723 507 700 484 721 483 722 484 722 483 722 507 698 506 699 507 699 507 698 507 698 1608 698 507 698 1607 699
#
name: Heat_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9074 4437 701 506 700 506 700 1607 702 1607 702 505 701 505 701 505 726 480 727 480 726 1582 726 1584 725 1583 724 481 726 481 725 482 724 483 724 482 724 482 725 482 724 482 725 483 724 1584 725 1584 725 482 724 482 726 482 724 482 725 483 724 1584 724 483 723 1585 723 483 724 482 725 1584 725 483 723 19925 724 482 724 482 724 482 725 482 725 482 725 483 724 482 724 482 724 482 724 1584 724 483 724 482 724 481 724 482 724 1585 724 1585 724 482 724 482 724 482 725 483 723 483 724 483 724 482 724 482 724 483 723 482 725 482 724 482 724 483 724 482 724 483 724 483 723 39945 9072 4414 725 481 726 481 725 482 724 482 725 481 725 482 724 482 724 482 725 482 724 483 724 483 724 483 723 482 725 482 724 482 724 483 723 482 724 483 724 483 723 483 724 483 724 482 725 482 725 483 724 483 724 482 725 483 724 482 724 482 725 1585 724 483 724 1584 724 482 724 1585 724 483 723 19929 723 482 724 482 725 482 724 483 724 483 723 483 723 482 724 482 724 482 724 482 724 482 725 482 724 482 724 483 724 483 723 482 724 482 724 483 724 483 723 482 725 483 724 482 724 482 725 482 724 483 724 483 724 483 724 483 724 482 724 1585 723 483 724 1585 724
#
name: Cool_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9150 4356 783 1525 815 419 787 419 759 1549 760 447 758 447 758 449 756 451 755 451 755 451 754 452 754 453 754 452 754 453 753 453 753 454 753 454 752 453 753 454 752 454 753 455 751 1557 725 1585 725 481 725 481 725 482 725 482 725 482 724 1584 725 482 724 1584 725 482 724 481 726 1584 724 482 724 19927 724 481 726 482 724 482 725 481 725 481 725 482 725 482 724 481 726 481 725 1584 724 481 725 481 725 482 725 481 725 1584 725 1585 724 482 725 482 724 482 725 482 724 482 725 482 724 481 725 482 725 482 724 482 725 482 724 482 724 1584 724 1584 724 1584 724 1585 725 39945 9071 4412 726 480 726 480 726 481 725 481 725 481 725 481 725 481 725 482 725 481 724 481 726 482 725 481 725 482 724 482 724 482 725 482 724 481 725 482 725 482 725 482 724 482 724 482 725 481 725 482 725 482 725 481 726 482 724 482 724 481 726 1584 725 482 724 1584 724 482 724 1585 724 482 725 19924 725 481 725 481 726 481 725 481 725 481 726 481 724 482 725 482 724 481 725 481 725 481 725 481 726 482 725 481 725 481 725 482 724 481 752 455 752 454 753 455 751 454 752 454 753 454 751 453 753 454 752 454 753 454 753 454 752 455 752 1556 753 453 754 1557 752
#
name: Cool_lo
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9049 4436 702 1606 703 504 730 476 759 1548 758 448 730 476 730 476 730 477 729 1580 727 1581 727 1581 727 480 726 481 725 481 725 481 724 482 724 507 699 483 724 483 723 507 699 506 701 1609 699 1609 699 507 700 507 699 507 700 507 699 507 700 1610 699 507 700 1609 700 507 699 507 700 1610 699 507 700 19928 723 482 724 483 723 483 723 484 722 507 699 507 700 507 699 507 699 507 700 1609 700 508 699 507 700 507 699 507 699 1609 700 1609 699 507 699 508 699 507 699 507 700 507 700 507 700 507 699 507 699 507 700 507 699 507 700 507 699 507 700 1610 699 1610 699 507 700 39944 9074 4410 727 479 727 480 726 481 726 481 725 482 725 483 723 483 724 507 699 507 700 484 722 507 699 507 699 507 699 506 700 507 699 507 700 507 699 507 698 507 699 508 699 507 699 507 699 507 700 507 700 507 698 507 699 508 699 507 699 507 699 1610 699 507 699 1610 699 507 699 1609 699 507 699 19926 724 483 723 506 700 507 699 507 700 507 700 507 700 507 699 507 700 507 700 507 699 507 700 507 699 507 699 507 700 506 700 508 699 507 700 507 700 507 699 507 699 507 700 507 700 507 700 508 699 507 699 507 699 508 699 507 699 507 700 1610 698 507 699 1609 699
#

View File

@@ -5649,12 +5649,6 @@ protocol: NEC
address: 80 00 00 00
command: 0A 00 00 00
#
name: Vol_dn
type: parsed
protocol: NECext
address: D9 14 00 00
command: 50 AF 00 00
#
# Model: Toshiba SBX4250
name: Power
type: parsed
@@ -5966,3 +5960,36 @@ type: raw
frequency: 38000
duty_cycle: 0.330000
data: 4549 4471 540 470 542 495 517 493 519 491 490 1482 512 1487 548 463 539 471 520 1479 566 1432 520 1479 515 1484 541 470 542 468 544 492 520 491 490 4492 539 497 515 495 517 494 518 492 489 1483 521 1478 547 463 518 1481 544 467 545 491 521 463 518 1481 544 466 546 465 516 1482 543 468 513 1486 518 1481 513 1486 538 472 540 55982 4553 4467 544 466 546 464 548 462 540 471 520 1478 516 1484 541 469 543 467 514 1485 519 1480 514 1486 518 1480 545 466 546 464 538 473 539 471 520 4488 543 467 545 465 547 463 539 472 519 1480 513 1485 540 471 520 1478 547 464 538 472 540 470 573 1426 547 463 539 472 519 1479 546 465 516 1483 511 1488 516 1483 542 469 543
#
# Model: JVC RM-SRVNB1A for JVC RV-NB1 boombox (2004)
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.333333
data: 8400 4200 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 25248 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 33664
#
name: Vol_up
type: raw
frequency: 38000
duty_cycle: 0.333333
data: 8400 4200 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 1578 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 25248 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 1578 526 526 526 526 526 526 526 526 526 526 526 526 526 526 526 33664
#
name: Vol_dn
type: raw
frequency: 38000
duty_cycle: 0.333333
data: 8400 4200 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 526 526 1578 526 526 526 526 526 526 526 526 526 526 526 526 526 25248 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 526 526 1578 526 526 526 526 526 526 526 526 526 526 526 526 526 33664
#
name: Next
type: raw
frequency: 38000
duty_cycle: 0.333333
data: 8400 4200 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 1578 526 526 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 25248 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 1578 526 526 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 33664
#
name: Prev
type: raw
frequency: 38000
duty_cycle: 0.333333
data: 8400 4200 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 526 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 25248 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 1578 526 526 526 1578 526 1578 526 1578 526 1578 526 1578 526 526 526 526 526 33664
#

View File

@@ -1812,12 +1812,6 @@ protocol: NECext
address: 86 6B 00 00
command: 09 F6 00 00
#
name: Vol_dn
type: parsed
protocol: NECext
address: 4F 50 00 00
command: 0B F4 00 00
#
# Model: RIF6-cube-projector-raw
#
name: Power

View File

@@ -7,6 +7,7 @@ App(
icon="A_125khz_14",
stack_size=2 * 1024,
order=20,
resources="resources",
fap_libs=["assets"],
fap_icon="icon.png",
fap_category="RFID",

View File

@@ -0,0 +1,252 @@
Filetype: Flipper LFRFID resources
Version: 1
# country code: 2-letter name 3-letter name full name
0004: AF AFG Afghanistan
0248: AX ALA Åland Islands
0008: AL ALB Albania
0012: DZ DZA Algeria
0016: AS ASM American Samoa
0020: AD AND Andorra
0024: AO AGO Angola
0660: AI AIA Anguilla
0010: AQ ATA Antarctica
0028: AG ATG Antigua and Barbuda
0032: AR ARG Argentina
0051: AM ARM Armenia
0533: AW ABW Aruba
0036: AU AUS Australia
0040: AT AUT Austria
0031: AZ AZE Azerbaijan
0044: BS BHS Bahamas
0048: BH BHR Bahrain
0050: BD BGD Bangladesh
0052: BB BRB Barbados
0112: BY BLR Belarus
0056: BE BEL Belgium
0084: BZ BLZ Belize
0204: BJ BEN Benin
0060: BM BMU Bermuda
0064: BT BTN Bhutan
0068: BO BOL Bolivia, Plurinational State of
0535: BQ BES Bonaire, Sint Eustatius and Saba
0070: BA BIH Bosnia and Herzegovina
0072: BW BWA Botswana
0074: BV BVT Bouvet Island
0076: BR BRA Brazil
0086: IO IOT British Indian Ocean Territory
0096: BN BRN Brunei Darussalam
0100: BG BGR Bulgaria
0854: BF BFA Burkina Faso
0108: BI BDI Burundi
0132: CV CPV Cabo Verde
0116: KH KHM Cambodia
0120: CM CMR Cameroon
0124: CA CAN Canada
0136: KY CYM Cayman Islands
0140: CF CAF Central African Republic
0148: TD TCD Chad
0152: CL CHL Chile
0156: CN CHN China
0162: CX CXR Christmas Island
0166: CC CCK Cocos (Keeling) Islands
0170: CO COL Colombia
0174: KM COM Comoros
0178: CG COG Congo
0180: CD COD Congo, Democratic Republic of the
0184: CK COK Cook Islands
0188: CR CRI Costa Rica
0384: CI CIV Côte d'Ivoire
0191: HR HRV Croatia
0192: CU CUB Cuba
0531: CW CUW Curaçao
0196: CY CYP Cyprus
0203: CZ CZE Czechia
0208: DK DNK Denmark
0262: DJ DJI Djibouti
0212: DM DMA Dominica
0214: DO DOM Dominican Republic
0218: EC ECU Ecuador
0818: EG EGY Egypt
0222: SV SLV El Salvador
0226: GQ GNQ Equatorial Guinea
0232: ER ERI Eritrea
0233: EE EST Estonia
0748: SZ SWZ Eswatini
0231: ET ETH Ethiopia
0238: FK FLK Falkland Islands (Malvinas)
0234: FO FRO Faroe Islands
0242: FJ FJI Fiji
0246: FI FIN Finland
0250: FR FRA France
0254: GF GUF French Guiana
0258: PF PYF French Polynesia
0260: TF ATF French Southern Territories
0266: GA GAB Gabon
0270: GM GMB Gambia
0268: GE GEO Georgia
0276: DE DEU Germany
0288: GH GHA Ghana
0292: GI GIB Gibraltar
0300: GR GRC Greece
0304: GL GRL Greenland
0308: GD GRD Grenada
0312: GP GLP Guadeloupe
0316: GU GUM Guam
0320: GT GTM Guatemala
0831: GG GGY Guernsey
0324: GN GIN Guinea
0624: GW GNB Guinea-Bissau
0328: GY GUY Guyana
0332: HT HTI Haiti
0334: HM HMD Heard Island and McDonald Islands
0336: VA VAT Holy See
0340: HN HND Honduras
0344: HK HKG Hong Kong
0348: HU HUN Hungary
0352: IS ISL Iceland
0356: IN IND India
0360: ID IDN Indonesia
0364: IR IRN Iran, Islamic Republic of
0368: IQ IRQ Iraq
0372: IE IRL Ireland
0833: IM IMN Isle of Man
0376: IL ISR Israel
0380: IT ITA Italy
0388: JM JAM Jamaica
0392: JP JPN Japan
0832: JE JEY Jersey
0400: JO JOR Jordan
0398: KZ KAZ Kazakhstan
0404: KE KEN Kenya
0296: KI KIR Kiribati
0408: KP PRK Korea, Democratic People's Republic of
0410: KR KOR Korea, Republic of
0414: KW KWT Kuwait
0417: KG KGZ Kyrgyzstan
0418: LA LAO Lao People's Democratic Republic
0428: LV LVA Latvia
0422: LB LBN Lebanon
0426: LS LSO Lesotho
0430: LR LBR Liberia
0434: LY LBY Libya
0438: LI LIE Liechtenstein
0440: LT LTU Lithuania
0442: LU LUX Luxembourg
0446: MO MAC Macao
0450: MG MDG Madagascar
0454: MW MWI Malawi
0458: MY MYS Malaysia
0462: MV MDV Maldives
0466: ML MLI Mali
0470: MT MLT Malta
0584: MH MHL Marshall Islands
0474: MQ MTQ Martinique
0478: MR MRT Mauritania
0480: MU MUS Mauritius
0175: YT MYT Mayotte
0484: MX MEX Mexico
0583: FM FSM Micronesia, Federated States of
0498: MD MDA Moldova, Republic of
0492: MC MCO Monaco
0496: MN MNG Mongolia
0499: ME MNE Montenegro
0500: MS MSR Montserrat
0504: MA MAR Morocco
0508: MZ MOZ Mozambique
0104: MM MMR Myanmar
0516: NA NAM Namibia
0520: NR NRU Nauru
0524: NP NPL Nepal
0528: NL NLD Netherlands, Kingdom of the
0540: NC NCL New Caledonia
0554: NZ NZL New Zealand
0558: NI NIC Nicaragua
0562: NE NER Niger
0566: NG NGA Nigeria
0570: NU NIU Niue
0574: NF NFK Norfolk Island
0807: MK MKD North Macedonia
0580: MP MNP Northern Mariana Islands
0578: NO NOR Norway
0512: OM OMN Oman
0586: PK PAK Pakistan
0585: PW PLW Palau
0275: PS PSE Palestine, State of
0591: PA PAN Panama
0598: PG PNG Papua New Guinea
0600: PY PRY Paraguay
0604: PE PER Peru
0608: PH PHL Philippines
0612: PN PCN Pitcairn
0616: PL POL Poland
0620: PT PRT Portugal
0630: PR PRI Puerto Rico
0634: QA QAT Qatar
0638: RE REU Réunion
0642: RO ROU Romania
0643: RU RUS Russian Federation
0646: RW RWA Rwanda
0652: BL BLM Saint Barthélemy
0654: SH SHN Saint Helena, Ascension and Tristan da Cunha
0659: KN KNA Saint Kitts and Nevis
0662: LC LCA Saint Lucia
0663: MF MAF Saint Martin (French part)
0666: PM SPM Saint Pierre and Miquelon
0670: VC VCT Saint Vincent and the Grenadines
0882: WS WSM Samoa
0674: SM SMR San Marino
0678: ST STP Sao Tome and Principe
0682: SA SAU Saudi Arabia
0686: SN SEN Senegal
0688: RS SRB Serbia
0690: SC SYC Seychelles
0694: SL SLE Sierra Leone
0702: SG SGP Singapore
0534: SX SXM Sint Maarten (Dutch part)
0703: SK SVK Slovakia
0705: SI SVN Slovenia
0090: SB SLB Solomon Islands
0706: SO SOM Somalia
0710: ZA ZAF South Africa
0239: GS SGS South Georgia and the South Sandwich Islands
0728: SS SSD South Sudan
0724: ES ESP Spain
0144: LK LKA Sri Lanka
0729: SD SDN Sudan
0740: SR SUR Suriname
0744: SJ SJM Svalbard and Jan Mayen
0752: SE SWE Sweden
0756: CH CHE Switzerland
0760: SY SYR Syrian Arab Republic
0158: TW TWN Taiwan, Province of China
0762: TJ TJK Tajikistan
0834: TZ TZA Tanzania, United Republic of
0764: TH THA Thailand
0626: TL TLS Timor-Leste
0768: TG TGO Togo
0772: TK TKL Tokelau
0776: TO TON Tonga
0780: TT TTO Trinidad and Tobago
0788: TN TUN Tunisia
0792: TR TUR Türkiye
0795: TM TKM Turkmenistan
0796: TC TCA Turks and Caicos Islands
0798: TV TUV Tuvalu
0800: UG UGA Uganda
0804: UA UKR Ukraine
0784: AE ARE United Arab Emirates
0826: GB GBR United Kingdom of Great Britain and Northern Ireland
0840: US USA United States of America
0581: UM UMI United States Minor Outlying Islands
0858: UY URY Uruguay
0860: UZ UZB Uzbekistan
0548: VU VUT Vanuatu
0862: VE VEN Venezuela, Bolivarian Republic of
0704: VN VNM Viet Nam
0092: VG VGB Virgin Islands (British)
0850: VI VIR Virgin Islands (U.S.)
0876: WF WLF Wallis and Futuna
0732: EH ESH Western Sahara
0887: YE YEM Yemen
0894: ZM ZMB Zambia
0716: ZW ZWE Zimbabwe

View File

@@ -8,11 +8,7 @@ App(
stack_size=5 * 1024,
order=30,
resources="resources",
sources=[
"*.c*",
"!plugins",
"!nfc_cli.c",
],
sources=["*.c*", "!plugins", "!nfc_cli.c", "!cli"],
fap_libs=["assets", "mbedtls"],
fap_icon="icon.png",
fap_category="NFC",
@@ -573,5 +569,62 @@ App(
apptype=FlipperAppType.PLUGIN,
entry_point="cli_nfc_ep",
requires=["cli"],
sources=["nfc_cli.c"],
sources=[
"helpers/mf_classic_key_cache.c",
"helpers/protocol_support/iso14443_3a/iso14443_3a_render.c",
"helpers/protocol_support/mf_ultralight/mf_ultralight_render.c",
"cli/nfc_cli.c",
"cli/nfc_cli_commands.c",
"cli/nfc_cli_command_processor.c",
"cli/commands/helpers/nfc_cli_format.c",
"cli/commands/helpers/nfc_cli_scanner.c",
"cli/commands/helpers/nfc_cli_protocol_parser.c",
"cli/commands/raw/nfc_cli_command_raw.c",
"cli/commands/raw/protocol_handlers/iso14443_3a/nfc_cli_raw_iso14443_3a.c",
"cli/commands/raw/protocol_handlers/iso14443_3b/nfc_cli_raw_iso14443_3b.c",
"cli/commands/raw/protocol_handlers/iso15693_3/nfc_cli_raw_iso15693_3.c",
"cli/commands/raw/protocol_handlers/felica/nfc_cli_raw_felica.c",
"cli/commands/apdu/nfc_cli_command_apdu.c",
"cli/commands/apdu/protocol_handlers/iso14443_4a/nfc_cli_apdu_iso14443_4a.c",
"cli/commands/apdu/protocol_handlers/iso14443_4b/nfc_cli_apdu_iso14443_4b.c",
"cli/commands/apdu/protocol_handlers/iso15693_3/nfc_cli_apdu_iso15693_3.c",
"cli/commands/dump/nfc_cli_command_dump.c",
"cli/commands/dump/protocols/iso14443_3a/nfc_cli_dump_iso14443_3a.c",
"cli/commands/dump/protocols/iso14443_3b/nfc_cli_dump_iso14443_3b.c",
"cli/commands/dump/protocols/iso14443_4a/nfc_cli_dump_iso14443_4a.c",
"cli/commands/dump/protocols/iso14443_4b/nfc_cli_dump_iso14443_4b.c",
"cli/commands/dump/protocols/iso15693_3/nfc_cli_dump_iso15693_3.c",
"cli/commands/dump/protocols/mf_ultralight/nfc_cli_dump_mf_ultralight.c",
"cli/commands/dump/protocols/mf_classic/nfc_cli_dump_mf_classic.c",
"cli/commands/dump/protocols/mf_plus/nfc_cli_dump_mf_plus.c",
"cli/commands/dump/protocols/mf_desfire/nfc_cli_dump_mf_desfire.c",
"cli/commands/dump/protocols/slix/nfc_cli_dump_slix.c",
"cli/commands/dump/protocols/st25tb/nfc_cli_dump_st25tb.c",
"cli/commands/dump/protocols/felica/nfc_cli_dump_felica.c",
"cli/commands/mfu/nfc_cli_command_mfu.c",
"cli/commands/mfu/nfc_cli_action_info.c",
"cli/commands/mfu/nfc_cli_action_rdbl.c",
"cli/commands/mfu/nfc_cli_action_wrbl.c",
"cli/commands/nfc_cli_command_emulate.c",
"cli/commands/nfc_cli_command_scanner.c",
"cli/commands/nfc_cli_command_field.c",
],
)
App(
appid="banapass_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="banapass_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/banapass.c"],
)
App(
appid="aic_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="aic_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/aic.c"],
)

View File

@@ -0,0 +1,311 @@
#include "nfc_cli_command_apdu.h"
#include "../helpers/nfc_cli_format.h"
#include "../helpers/nfc_cli_protocol_parser.h"
#include "protocol_handlers/iso14443_4a/nfc_cli_apdu_iso14443_4a.h"
#include "protocol_handlers/iso14443_4b/nfc_cli_apdu_iso14443_4b.h"
#include "protocol_handlers/iso15693_3/nfc_cli_apdu_iso15693_3.h"
#include <furi.h>
#include <nfc/nfc.h>
#include <nfc/nfc_poller.h>
#include <toolbox/args.h>
#include <m-array.h>
#include <m-algo.h>
#define TAG "APDU"
#define NFC_CLI_PROTOCOL_SUPPORT_MAX_BUFFER_SIZE (256)
typedef NfcCommand (
*NfcCliApduProtocolHandler)(NfcGenericEvent event, NfcCliApduRequestResponse* instance);
static const char* raw_error_names[] = {
[NfcCliApduErrorNone] = "None",
[NfcCliApduErrorTimeout] = "Timeout",
[NfcCliApduErrorProtocol] = "Internal protocol",
[NfcCliApduErrorWrongCrc] = "Wrong CRC",
[NfcCliApduErrorNotPresent] = "No card",
};
typedef enum {
NfcCliProtocolRequestTypeNormalExecute,
NfcCliProtocolRequestTypeAbort,
} NfcCliProtocolRequestType;
typedef struct {
uint8_t* data;
size_t size;
} NfcCliApduData;
static void ApduItem_init(NfcCliApduData* item) {
item->size = 0;
item->data = NULL;
}
static void ApduItem_init_set(NfcCliApduData* item, const NfcCliApduData* src) {
item->data = malloc(src->size);
item->size = src->size;
memcpy(item->data, src->data, src->size);
}
static void ApduItem_set(NfcCliApduData* item, const NfcCliApduData* src) {
if(item->data == NULL) {
item->data = malloc(src->size);
} else if(item->size != src->size) {
uint8_t* buf = realloc(item->data, src->size);
furi_check(buf);
item->data = buf;
}
item->size = src->size;
memcpy(item->data, src->data, src->size);
}
static void ApduItem_clear(NfcCliApduData* item) {
if(item->data) free(item->data);
item->data = NULL;
item->size = 0;
}
ARRAY_DEF(
NfcCliApduItemArray,
NfcCliApduData,
(INIT(API_2(ApduItem_init)),
SET(API_6(ApduItem_set)),
INIT_SET(API_6(ApduItem_init_set)),
CLEAR(API_2(ApduItem_clear))))
typedef struct {
Nfc* nfc;
bool auto_detect;
NfcCliApduItemArray_t apdu;
NfcCliApduRequestResponse data;
FuriSemaphore* sem_done;
FuriMessageQueue* input_queue;
} NfcCliApduContext;
static NfcCliActionContext* nfc_cli_apdu_alloc_ctx(Nfc* nfc) {
furi_assert(nfc);
NfcCliApduContext* instance = malloc(sizeof(NfcCliApduContext));
instance->nfc = nfc;
instance->data.protocol = NfcProtocolInvalid;
instance->auto_detect = true;
NfcCliApduItemArray_init(instance->apdu);
instance->data.rx_buffer = bit_buffer_alloc(NFC_CLI_PROTOCOL_SUPPORT_MAX_BUFFER_SIZE);
instance->data.tx_buffer = bit_buffer_alloc(NFC_CLI_PROTOCOL_SUPPORT_MAX_BUFFER_SIZE);
instance->sem_done = furi_semaphore_alloc(1, 0);
instance->input_queue = furi_message_queue_alloc(1, sizeof(NfcCliProtocolRequestType));
return instance;
}
static void nfc_cli_apdu_free_ctx(NfcCliActionContext* action_ctx) {
furi_assert(action_ctx);
NfcCliApduContext* instance = action_ctx;
instance->nfc = NULL;
NfcCliApduItemArray_clear(instance->apdu);
bit_buffer_free(instance->data.rx_buffer);
bit_buffer_free(instance->data.tx_buffer);
furi_semaphore_free(instance->sem_done);
furi_message_queue_free(instance->input_queue);
free(instance);
}
static inline void nfc_cli_apdu_print_result(const NfcCliApduContext* instance) {
nfc_cli_printf_array(
bit_buffer_get_data(instance->data.tx_buffer),
bit_buffer_get_size_bytes(instance->data.tx_buffer),
"\r\nTx: ");
if(instance->data.result != NfcCliApduErrorNone)
printf("\r\nError: \"%s\"\r\n", raw_error_names[instance->data.result]);
size_t rx_size = bit_buffer_get_size_bytes(instance->data.rx_buffer);
if(rx_size > 0) {
nfc_cli_printf_array(
bit_buffer_get_data(instance->data.rx_buffer),
bit_buffer_get_size_bytes(instance->data.rx_buffer),
"\r\nRx: ");
printf("\r\n");
}
}
static NfcProtocol nfc_cli_apdu_protocol_autodetect(Nfc* nfc) {
const NfcProtocol supported_protocols[] = {
NfcProtocolIso14443_4a,
NfcProtocolIso14443_4b,
NfcProtocolIso15693_3,
};
const char* supported_names[] = {"Iso14443_4a", "Iso14443_4b", "Iso15693_3"};
NfcProtocol protocol = NfcProtocolInvalid;
for(uint8_t i = 0; i < COUNT_OF(supported_protocols); i++) {
NfcPoller* poller = nfc_poller_alloc(nfc, supported_protocols[i]);
bool is_detected = nfc_poller_detect(poller);
nfc_poller_free(poller);
if(is_detected) {
protocol = supported_protocols[i];
printf("Detected tag: %s\r\n", supported_names[i]);
break;
}
}
return protocol;
}
static NfcCliApduProtocolHandler nfc_cli_apdu_poller_get_handler(NfcProtocol protocol) {
if(protocol == NfcProtocolIso14443_4a)
return nfc_cli_apdu_iso14443_4a_handler;
else if(protocol == NfcProtocolIso14443_4b)
return nfc_cli_apdu_iso14443_4b_handler;
else if(protocol == NfcProtocolIso15693_3)
return nfc_cli_apdu_iso15693_3_handler;
else
return NULL;
}
static NfcCommand nfc_cli_apdu_poller_callback(NfcGenericEvent event, void* context) {
NfcCliApduContext* instance = context;
FURI_LOG_D(TAG, "Poller callback");
NfcCliProtocolRequestType request_type = NfcCliProtocolRequestTypeAbort;
furi_message_queue_get(instance->input_queue, &request_type, FuriWaitForever);
NfcCommand command = NfcCommandStop;
if(request_type == NfcCliProtocolRequestTypeAbort) {
FURI_LOG_D(TAG, "Aborting poller callback");
} else {
NfcCliApduProtocolHandler handler =
nfc_cli_apdu_poller_get_handler(instance->data.protocol);
if(handler) command = handler(event, &instance->data);
}
furi_semaphore_release(instance->sem_done);
return command;
}
static void nfc_cli_apdu_execute(PipeSide* pipe, void* context) {
UNUSED(pipe);
furi_assert(context);
NfcCliApduContext* instance = context;
if(instance->auto_detect) {
instance->data.protocol = nfc_cli_apdu_protocol_autodetect(instance->nfc);
}
if(instance->data.protocol != NfcProtocolInvalid) {
NfcPoller* poller = nfc_poller_alloc(instance->nfc, instance->data.protocol);
NfcCliProtocolRequestType request_type = NfcCliProtocolRequestTypeNormalExecute;
nfc_poller_start(poller, nfc_cli_apdu_poller_callback, instance);
NfcCliApduItemArray_it_t it;
for(NfcCliApduItemArray_it(it, instance->apdu); !NfcCliApduItemArray_end_p(it);
NfcCliApduItemArray_next(it)) {
const NfcCliApduData* item = NfcCliApduItemArray_cref(it);
bit_buffer_copy_bytes(instance->data.tx_buffer, item->data, item->size);
bit_buffer_reset(instance->data.rx_buffer);
furi_message_queue_put(instance->input_queue, &request_type, FuriWaitForever);
furi_semaphore_acquire(instance->sem_done, FuriWaitForever);
nfc_cli_apdu_print_result(instance);
if(instance->data.result != NfcCliApduErrorNone) break;
}
request_type = NfcCliProtocolRequestTypeAbort;
furi_message_queue_put(instance->input_queue, &request_type, FuriWaitForever);
nfc_poller_stop(poller);
nfc_poller_free(poller);
}
}
static const NfcProtocolNameValuePair supported_protocols[] = {
{.name = "4a", .value = NfcProtocolIso14443_4a},
{.name = "4b", .value = NfcProtocolIso14443_4b},
{.name = "15", .value = NfcProtocolIso15693_3},
};
static bool nfc_cli_apdu_parse_protocol(FuriString* value, void* output) {
NfcCliApduContext* ctx = output;
ctx->auto_detect = false;
NfcCliProtocolParser* parser =
nfc_cli_protocol_parser_alloc(supported_protocols, COUNT_OF(supported_protocols));
bool result = nfc_cli_protocol_parser_get(parser, value, &ctx->data.protocol);
nfc_cli_protocol_parser_free(parser);
return result;
}
static bool nfc_cli_apdu_parse_data(FuriString* value, void* output) {
NfcCliApduContext* ctx = output;
bool result = false;
FuriString* word = furi_string_alloc();
while(args_read_string_and_trim(value, word)) {
size_t len = furi_string_size(word);
if(len % 2 != 0) break;
size_t data_length = len / 2;
const size_t max_len = UINT16_MAX;
if(data_length > max_len) {
printf(
ANSI_FG_RED "\r\nData payload is too long, max length = %d bytes\r\n" ANSI_RESET,
max_len);
break;
}
NfcCliApduData* item = NfcCliApduItemArray_push_new(ctx->apdu);
item->size = data_length;
item->data = malloc(data_length);
result = args_read_hex_bytes(word, item->data, item->size);
}
furi_string_free(word);
return result;
}
const NfcCliKeyDescriptor apdu_keys[] = {
{
.long_name = "protocol",
.short_name = "p",
.description = "set protocol (4a, 4b, 15) directly, otherwise autodetected",
.features = {.parameter = true, .required = false},
.parse = nfc_cli_apdu_parse_protocol,
},
{
.long_name = "data",
.short_name = "d",
.description = "apdu payloads in format p1 p2 p3",
.features = {.parameter = true, .multivalue = true, .required = true},
.parse = nfc_cli_apdu_parse_data,
},
};
const NfcCliActionDescriptor apdu_action = {
.name = "apdu",
.description = "Send APDU data to iso14443_4a, iso14443_4b or iso15693_3",
.alloc = nfc_cli_apdu_alloc_ctx,
.free = nfc_cli_apdu_free_ctx,
.execute = nfc_cli_apdu_execute,
.key_count = COUNT_OF(apdu_keys),
.keys = apdu_keys,
};
const NfcCliActionDescriptor* apdu_actions_collection[] = {&apdu_action};
//Command descriptor
ADD_NFC_CLI_COMMAND(apdu, "", apdu_actions_collection);
//Command usage: apdu <protocol> <data>
//Command examples:
//apdu -d 00a404000e325041592e5359532e444446303100 00A4040008A000000333010102
//apdu -p 4a -d 00a404000e325041592e5359532e444446303100 00A4040008A000000333010102
//apdu -p 4b -d 00a404000e325041592e5359532e444446303100 00A4040008A000000333010102
//apdu -p 15 -d 00a404000e325041592e5359532e444446303100 00A4040008A000000333010102

View File

@@ -0,0 +1,5 @@
#pragma once
#include "../../nfc_cli_command_base_i.h"
extern const NfcCliCommandDescriptor apdu_cmd;

View File

@@ -0,0 +1,37 @@
#include "nfc_cli_apdu_iso14443_4a.h"
#include "../../../helpers/nfc_cli_format.h"
#include <nfc/protocols/iso14443_4a/iso14443_4a.h>
#include <nfc/protocols/iso14443_4a/iso14443_4a_poller.h>
#define TAG "ISO14A_4A"
#define BIT_BUFFER_EMPTY(buffer) ((bit_buffer_get_size_bytes(buffer) == 0))
static NfcCliApduError nfc_cli_apdu_iso14443_4a_process_error(Iso14443_4aError error) {
switch(error) {
case Iso14443_4aErrorNone:
return NfcCliApduErrorNone;
case Iso14443_4aErrorTimeout:
return NfcCliApduErrorTimeout;
case Iso14443_4aErrorNotPresent:
return NfcCliApduErrorNotPresent;
default:
return NfcCliApduErrorProtocol;
}
}
NfcCommand
nfc_cli_apdu_iso14443_4a_handler(NfcGenericEvent event, NfcCliApduRequestResponse* instance) {
Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) {
Iso14443_4aError err = iso14443_4a_poller_send_block(
event.instance, instance->tx_buffer, instance->rx_buffer);
instance->result = nfc_cli_apdu_iso14443_4a_process_error(err);
if(err != Iso14443_4aErrorNone) command = NfcCommandStop;
}
return command;
}

View File

@@ -0,0 +1,6 @@
#pragma once
#include "../nfc_cli_apdu_common_types.h"
NfcCommand
nfc_cli_apdu_iso14443_4a_handler(NfcGenericEvent event, NfcCliApduRequestResponse* instance);

View File

@@ -0,0 +1,37 @@
#include "nfc_cli_apdu_iso14443_4b.h"
#include "../../../helpers/nfc_cli_format.h"
#include <nfc/protocols/iso14443_4b/iso14443_4b.h>
#include <nfc/protocols/iso14443_4b/iso14443_4b_poller.h>
#define TAG "ISO14A_4B"
#define BIT_BUFFER_EMPTY(buffer) ((bit_buffer_get_size_bytes(buffer) == 0))
static NfcCliApduError nfc_cli_apdu_iso14443_4b_process_error(Iso14443_4bError error) {
switch(error) {
case Iso14443_4bErrorNone:
return NfcCliApduErrorNone;
case Iso14443_4bErrorTimeout:
return NfcCliApduErrorTimeout;
case Iso14443_4bErrorNotPresent:
return NfcCliApduErrorNotPresent;
default:
return NfcCliApduErrorProtocol;
}
}
NfcCommand
nfc_cli_apdu_iso14443_4b_handler(NfcGenericEvent event, NfcCliApduRequestResponse* instance) {
Iso14443_4bPollerEvent* iso14443_4b_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(iso14443_4b_event->type == Iso14443_4bPollerEventTypeReady) {
Iso14443_4bError err = iso14443_4b_poller_send_block(
event.instance, instance->tx_buffer, instance->rx_buffer);
instance->result = nfc_cli_apdu_iso14443_4b_process_error(err);
if(err != Iso14443_4bErrorNone) command = NfcCommandStop;
}
return command;
}

View File

@@ -0,0 +1,6 @@
#pragma once
#include "../nfc_cli_apdu_common_types.h"
NfcCommand
nfc_cli_apdu_iso14443_4b_handler(NfcGenericEvent event, NfcCliApduRequestResponse* instance);

View File

@@ -0,0 +1,37 @@
#include "nfc_cli_apdu_iso15693_3.h"
#include "../../../helpers/nfc_cli_format.h"
#include <nfc/protocols/iso15693_3/iso15693_3.h>
#include <nfc/protocols/iso15693_3/iso15693_3_poller.h>
#define TAG "ISO15"
#define BIT_BUFFER_EMPTY(buffer) ((bit_buffer_get_size_bytes(buffer) == 0))
static NfcCliApduError nfc_cli_apdu_iso15693_3_process_error(Iso15693_3Error error) {
switch(error) {
case Iso15693_3ErrorNone:
return NfcCliApduErrorNone;
case Iso15693_3ErrorTimeout:
return NfcCliApduErrorTimeout;
case Iso15693_3ErrorNotPresent:
return NfcCliApduErrorNotPresent;
default:
return NfcCliApduErrorProtocol;
}
}
NfcCommand
nfc_cli_apdu_iso15693_3_handler(NfcGenericEvent event, NfcCliApduRequestResponse* instance) {
Iso15693_3PollerEvent* iso15693_3_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(iso15693_3_event->type == Iso15693_3PollerEventTypeReady) {
Iso15693_3Error err = iso15693_3_poller_send_frame(
event.instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC);
instance->result = nfc_cli_apdu_iso15693_3_process_error(err);
if(err != Iso15693_3ErrorNone) command = NfcCommandStop;
}
return command;
}

View File

@@ -0,0 +1,6 @@
#pragma once
#include "../nfc_cli_apdu_common_types.h"
NfcCommand
nfc_cli_apdu_iso15693_3_handler(NfcGenericEvent event, NfcCliApduRequestResponse* instance);

View File

@@ -0,0 +1,20 @@
#pragma once
#include <furi.h>
#include <nfc/nfc.h>
#include <nfc/nfc_poller.h>
typedef enum {
NfcCliApduErrorNone,
NfcCliApduErrorTimeout,
NfcCliApduErrorNotPresent,
NfcCliApduErrorWrongCrc,
NfcCliApduErrorProtocol,
} NfcCliApduError;
typedef struct {
NfcProtocol protocol;
BitBuffer* tx_buffer;
BitBuffer* rx_buffer;
NfcCliApduError result;
} NfcCliApduRequestResponse;

View File

@@ -0,0 +1,319 @@
#include "nfc_cli_command_dump.h"
#include "protocols/nfc_cli_dump_common_types.h"
#include "../helpers/nfc_cli_format.h"
#include "../helpers/nfc_cli_protocol_parser.h"
#include "../helpers/nfc_cli_scanner.h"
#include "protocols/iso14443_3a/nfc_cli_dump_iso14443_3a.h"
#include "protocols/iso14443_3b/nfc_cli_dump_iso14443_3b.h"
#include "protocols/iso14443_4a/nfc_cli_dump_iso14443_4a.h"
#include "protocols/iso14443_4b/nfc_cli_dump_iso14443_4b.h"
#include "protocols/iso15693_3/nfc_cli_dump_iso15693_3.h"
#include "protocols/mf_classic/nfc_cli_dump_mf_classic.h"
#include "protocols/mf_desfire/nfc_cli_dump_mf_desfire.h"
#include "protocols/mf_plus/nfc_cli_dump_mf_plus.h"
#include "protocols/mf_ultralight/nfc_cli_dump_mf_ultralight.h"
#include "protocols/slix/nfc_cli_dump_slix.h"
#include "protocols/st25tb/nfc_cli_dump_st25tb.h"
#include "protocols/felica/nfc_cli_dump_felica.h"
#include <datetime.h>
#include <furi_hal_rtc.h>
#include <toolbox/strint.h>
#include <toolbox/path.h>
#include <toolbox/args.h>
#define NFC_DEFAULT_FOLDER EXT_PATH("nfc")
#define NFC_FILE_EXTENSION ".nfc"
#define NFC_CLI_DEFAULT_FILENAME_PREFIX "dump"
#define NFC_CLI_DUMP_DEFAULT_TIMEOUT (5000)
#define TAG "DUMP"
static const char* nfc_cli_dump_error_names[NfcCliDumpErrorNum] = {
[NfcCliDumpErrorNone] = "",
[NfcCliDumpErrorNotPresent] = "card not present",
[NfcCliDumpErrorAuthFailed] = "authentication failed",
[NfcCliDumpErrorTimeout] = "timeout",
[NfcCliDumpErrorFailedToRead] = "failed to read",
};
static NfcCliActionContext* nfc_cli_dump_alloc_ctx(Nfc* nfc) {
furi_assert(nfc);
NfcCliDumpContext* instance = malloc(sizeof(NfcCliDumpContext));
instance->nfc = nfc;
instance->file_path = furi_string_alloc();
instance->storage = furi_record_open(RECORD_STORAGE);
instance->sem_done = furi_semaphore_alloc(1, 0);
instance->nfc_device = nfc_device_alloc();
instance->desired_protocol = NfcProtocolInvalid;
instance->auth_ctx.skip_auth = true;
instance->auth_ctx.key_size = 0;
instance->timeout = NFC_CLI_DUMP_DEFAULT_TIMEOUT;
instance->mfc_key_cache = mf_classic_key_cache_alloc();
instance->scanner = nfc_cli_scanner_alloc(nfc);
return instance;
}
static void nfc_cli_dump_free_ctx(NfcCliActionContext* ctx) {
furi_assert(ctx);
NfcCliDumpContext* instance = ctx;
instance->desired_protocol = NfcProtocolInvalid;
furi_string_free(instance->file_path);
instance->nfc = NULL;
furi_record_close(RECORD_STORAGE);
furi_semaphore_free(instance->sem_done);
nfc_device_free(instance->nfc_device);
mf_classic_key_cache_free(instance->mfc_key_cache);
nfc_cli_scanner_free(instance->scanner);
free(instance);
}
static bool nfc_cli_dump_parse_filename_key(FuriString* value, void* output) {
furi_assert(value);
furi_assert(output);
NfcCliDumpContext* ctx = output;
furi_string_set(ctx->file_path, value);
return true;
}
NfcGenericCallback protocol_poller_callbacks[NfcProtocolNum] = {
[NfcProtocolMfUltralight] = nfc_cli_dump_poller_callback_mf_ultralight,
[NfcProtocolMfClassic] = nfc_cli_dump_poller_callback_mf_classic,
[NfcProtocolFelica] = nfc_cli_dump_poller_callback_felica,
[NfcProtocolIso14443_3a] = nfc_cli_dump_poller_callback_iso14443_3a,
[NfcProtocolIso14443_3b] = nfc_cli_dump_poller_callback_iso14443_3b,
[NfcProtocolIso14443_4a] = nfc_cli_dump_poller_callback_iso14443_4a,
[NfcProtocolIso14443_4b] = nfc_cli_dump_poller_callback_iso14443_4b,
[NfcProtocolIso15693_3] = nfc_cli_dump_poller_callback_iso15693_3,
[NfcProtocolSlix] = nfc_cli_dump_poller_callback_slix,
[NfcProtocolMfDesfire] = nfc_cli_dump_poller_callback_mf_desfire,
[NfcProtocolMfPlus] = nfc_cli_dump_poller_callback_mf_plus,
[NfcProtocolSt25tb] = nfc_cli_dump_poller_callback_st25tb,
};
static void nfc_cli_dump_generate_filename(FuriString* file_path) {
furi_string_set_str(file_path, NFC_DEFAULT_FOLDER);
DateTime dt;
furi_hal_rtc_get_datetime(&dt);
furi_string_cat_printf(
file_path,
"/%s-%.4d%.2d%.2d-%.2d%.2d%.2d%s",
NFC_CLI_DEFAULT_FILENAME_PREFIX,
dt.year,
dt.month,
dt.day,
dt.hour,
dt.minute,
dt.second,
NFC_FILE_EXTENSION);
}
static bool nfc_cli_dump_check_filepath_valid(FuriString* file_path, Storage* storage) {
bool file_exists = false;
bool dir_exists = false;
FuriString* buf = furi_string_alloc();
path_extract_dirname(furi_string_get_cstr(file_path), buf);
dir_exists = storage_dir_exists(storage, furi_string_get_cstr(buf));
file_exists = storage_file_exists(storage, furi_string_get_cstr(file_path));
bool result = true;
if(!dir_exists) {
printf(ANSI_FG_RED "Path \'%s\' doesn't exist\r\n" ANSI_RESET, furi_string_get_cstr(buf));
result = false;
} else if(file_exists) {
printf(
ANSI_FG_RED "File \'%s\' already exists\r\n" ANSI_RESET,
furi_string_get_cstr(file_path));
result = false;
}
furi_string_free(buf);
return result;
}
static bool nfc_cli_dump_process_filename(NfcCliDumpContext* instance) {
bool result = false;
if(furi_string_empty(instance->file_path)) {
nfc_cli_dump_generate_filename(instance->file_path);
result = true;
} else {
result = nfc_cli_dump_check_filepath_valid(instance->file_path, instance->storage);
}
return result;
}
static size_t nfc_cli_dump_set_protocol(NfcCliDumpContext* instance) {
size_t protocol_count = 0;
if(instance->desired_protocol != NfcProtocolInvalid) {
protocol_count = 1;
} else {
if(!nfc_cli_scanner_detect_protocol(instance->scanner, instance->timeout)) {
NfcCliDumpError error = NfcCliDumpErrorTimeout;
printf(ANSI_FG_RED "Error: %s\r\n" ANSI_RESET, nfc_cli_dump_error_names[error]);
} else {
nfc_cli_scanner_list_detected_protocols(instance->scanner);
protocol_count = nfc_cli_scanner_detected_protocol_num(instance->scanner);
instance->desired_protocol = nfc_cli_scanner_get_protocol(instance->scanner, 0);
}
}
return protocol_count;
}
static bool nfc_cli_dump_card(NfcCliDumpContext* instance) {
instance->poller = nfc_poller_alloc(instance->nfc, instance->desired_protocol);
NfcGenericCallback callback = protocol_poller_callbacks[instance->desired_protocol];
if(callback) {
nfc_poller_start(instance->poller, callback, instance);
FuriStatus status = furi_semaphore_acquire(instance->sem_done, instance->timeout);
if(status == FuriStatusErrorTimeout) instance->result = NfcCliDumpErrorTimeout;
nfc_poller_stop(instance->poller);
}
nfc_poller_free(instance->poller);
return instance->result == NfcCliDumpErrorNone;
}
static void nfc_cli_dump_execute(PipeSide* pipe, NfcCliActionContext* context) {
UNUSED(pipe);
furi_assert(context);
NfcCliDumpContext* instance = context;
do {
if(!nfc_cli_dump_process_filename(instance)) break;
size_t protocol_count = nfc_cli_dump_set_protocol(instance);
if(instance->desired_protocol == NfcProtocolInvalid) break;
printf("Dumping as \"%s\"\r\n", nfc_cli_get_protocol_name(instance->desired_protocol));
if(protocol_count > 1) printf("Use \'-p\' key to specify another protocol\r\n");
if(nfc_cli_dump_card(instance)) {
const char* path = furi_string_get_cstr(instance->file_path);
if(nfc_device_save(instance->nfc_device, path)) {
printf("Dump saved to \'%s\'\r\n", path);
}
} else {
printf(
ANSI_FG_RED "Error: %s\r\n" ANSI_RESET,
nfc_cli_dump_error_names[instance->result]);
}
} while(false);
}
static const NfcProtocolNameValuePair supported_protocols[] = {
{.name = "14_3a", .value = NfcProtocolIso14443_3a},
{.name = "14_3b", .value = NfcProtocolIso14443_3b},
{.name = "14_4a", .value = NfcProtocolIso14443_4a},
{.name = "14_4b", .value = NfcProtocolIso14443_4b},
{.name = "15", .value = NfcProtocolIso15693_3},
{.name = "felica", .value = NfcProtocolFelica},
{.name = "mfu", .value = NfcProtocolMfUltralight},
{.name = "mfc", .value = NfcProtocolMfClassic},
{.name = "mfp", .value = NfcProtocolMfPlus},
{.name = "des", .value = NfcProtocolMfDesfire},
{.name = "slix", .value = NfcProtocolSlix},
{.name = "st25", .value = NfcProtocolSt25tb},
};
static bool nfc_cli_dump_parse_protocol(FuriString* value, void* output) {
furi_assert(value);
furi_assert(output);
NfcCliDumpContext* ctx = output;
NfcCliProtocolParser* parser =
nfc_cli_protocol_parser_alloc(supported_protocols, COUNT_OF(supported_protocols));
bool result = nfc_cli_protocol_parser_get(parser, value, &ctx->desired_protocol);
nfc_cli_protocol_parser_free(parser);
return result;
}
static bool nfc_cli_dump_parse_key(FuriString* value, void* output) {
furi_assert(value);
furi_assert(output);
NfcCliDumpContext* ctx = output;
NfcCliDumpAuthContext* auth_ctx = &ctx->auth_ctx;
bool result = false;
do {
size_t len = furi_string_size(value);
if(len % 2 != 0) break;
size_t data_length = len / 2;
if(data_length != MF_ULTRALIGHT_AUTH_PASSWORD_SIZE &&
data_length != MF_ULTRALIGHT_C_AUTH_DES_KEY_SIZE) {
printf(ANSI_FG_RED "Error: Wrong key size" ANSI_RESET);
break;
}
if(!args_read_hex_bytes(value, auth_ctx->key.key, data_length)) break;
auth_ctx->key_size = data_length;
auth_ctx->skip_auth = false;
result = true;
} while(false);
return result;
}
bool nfc_cli_dump_parse_timeout(FuriString* value, NfcCliActionContext* output) {
NfcCliDumpContext* ctx = output;
StrintParseError err = strint_to_uint32(furi_string_get_cstr(value), NULL, &ctx->timeout, 10);
return err == StrintParseNoError;
}
const NfcCliKeyDescriptor dump_keys[] = {
{
.long_name = "key",
.short_name = "k",
.description = "key to path auth in protocols which requires it",
.features = {.required = false, .parameter = true},
.parse = nfc_cli_dump_parse_key,
},
{
.long_name = "protocol",
.short_name = "p",
.description = "desired protocol",
.features = {.required = false, .parameter = true},
.parse = nfc_cli_dump_parse_protocol,
},
{
.features = {.required = false, .parameter = true},
.long_name = "file",
.short_name = "f",
.description = "path to new file",
.parse = nfc_cli_dump_parse_filename_key,
},
{
.features = {.required = false, .parameter = true},
.long_name = "timeout",
.short_name = "t",
.description = "timeout value in milliseconds",
.parse = nfc_cli_dump_parse_timeout,
},
};
const NfcCliActionDescriptor dump_action = {
.name = "dump",
.description = "Dump tag to .nfc file",
.alloc = nfc_cli_dump_alloc_ctx,
.free = nfc_cli_dump_free_ctx,
.execute = nfc_cli_dump_execute,
.key_count = COUNT_OF(dump_keys),
.keys = dump_keys,
};
const NfcCliActionDescriptor* dump_actions_collection[] = {&dump_action};
//Command descriptor
ADD_NFC_CLI_COMMAND(dump, "", dump_actions_collection);
//Command examples:
//dump -f ext/nfc/test.nfc

View File

@@ -0,0 +1,5 @@
#pragma once
#include "../../nfc_cli_command_base_i.h"
extern const NfcCliCommandDescriptor dump_cmd;

View File

@@ -0,0 +1,32 @@
#include "nfc_cli_dump_felica.h"
#include <nfc/protocols/felica/felica_poller.h>
NfcCommand nfc_cli_dump_poller_callback_felica(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolFelica);
NfcCliDumpContext* instance = context;
const FelicaPollerEvent* felica_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(felica_event->type == FelicaPollerEventTypeReady ||
felica_event->type == FelicaPollerEventTypeIncomplete) {
nfc_device_set_data(
instance->nfc_device, NfcProtocolFelica, nfc_poller_get_data(instance->poller));
command = NfcCommandStop;
instance->result = NfcCliDumpErrorNone;
} else if(felica_event->type == FelicaPollerEventTypeError) {
command = NfcCommandStop;
instance->result = NfcCliDumpErrorFailedToRead;
} else if(felica_event->type == FelicaPollerEventTypeRequestAuthContext) {
FelicaAuthenticationContext* ctx = felica_event->data->auth_context;
const NfcCliDumpAuthContext* dump_auth_ctx = &instance->auth_ctx;
ctx->skip_auth = dump_auth_ctx->skip_auth;
ctx->card_key = dump_auth_ctx->key.felica_key;
}
if(command == NfcCommandStop) {
furi_semaphore_release(instance->sem_done);
}
return command;
}

View File

@@ -0,0 +1,5 @@
#pragma once
#include "../nfc_cli_dump_common_types.h"
NfcCommand nfc_cli_dump_poller_callback_felica(NfcGenericEvent event, void* context);

View File

@@ -0,0 +1,25 @@
#include "nfc_cli_dump_iso14443_3a.h"
#include <nfc/protocols/iso14443_3a/iso14443_3a_poller.h>
NfcCommand nfc_cli_dump_poller_callback_iso14443_3a(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolIso14443_3a);
NfcCliDumpContext* instance = context;
const Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) {
nfc_device_set_data(
instance->nfc_device, NfcProtocolIso14443_3a, nfc_poller_get_data(instance->poller));
command = NfcCommandStop;
} else if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeError) {
command = NfcCommandStop;
instance->result = NfcCliDumpErrorFailedToRead;
}
if(command == NfcCommandStop) {
furi_semaphore_release(instance->sem_done);
}
return command;
}

View File

@@ -0,0 +1,5 @@
#pragma once
#include "../nfc_cli_dump_common_types.h"
NfcCommand nfc_cli_dump_poller_callback_iso14443_3a(NfcGenericEvent event, void* context);

View File

@@ -0,0 +1,27 @@
#include "nfc_cli_dump_iso14443_3b.h"
#include <nfc/protocols/iso14443_3b/iso14443_3b_poller.h>
NfcCommand nfc_cli_dump_poller_callback_iso14443_3b(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolIso14443_3b);
NfcCliDumpContext* instance = context;
const Iso14443_3bPollerEvent* iso14443_3b_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(iso14443_3b_event->type == Iso14443_3bPollerEventTypeReady) {
nfc_device_set_data(
instance->nfc_device, NfcProtocolIso14443_3b, nfc_poller_get_data(instance->poller));
instance->result = NfcCliDumpErrorNone;
command = NfcCommandStop;
} else if(iso14443_3b_event->type == Iso14443_3bPollerEventTypeError) {
instance->result = NfcCliDumpErrorFailedToRead;
command = NfcCommandStop;
}
if(command == NfcCommandStop) {
furi_semaphore_release(instance->sem_done);
}
return NfcCommandContinue;
}

View File

@@ -0,0 +1,5 @@
#pragma once
#include "../nfc_cli_dump_common_types.h"
NfcCommand nfc_cli_dump_poller_callback_iso14443_3b(NfcGenericEvent event, void* context);

View File

@@ -0,0 +1,26 @@
#include "nfc_cli_dump_iso14443_4a.h"
#include <nfc/protocols/iso14443_4a/iso14443_4a_poller.h>
NfcCommand nfc_cli_dump_poller_callback_iso14443_4a(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolIso14443_4a);
NfcCliDumpContext* instance = context;
const Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) {
nfc_device_set_data(
instance->nfc_device, NfcProtocolIso14443_4a, nfc_poller_get_data(instance->poller));
instance->result = NfcCliDumpErrorNone;
command = NfcCommandStop;
} else if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeError) {
instance->result = NfcCliDumpErrorFailedToRead;
command = NfcCommandStop;
}
if(command == NfcCommandStop) {
furi_semaphore_release(instance->sem_done);
}
return command;
}

View File

@@ -0,0 +1,5 @@
#pragma once
#include "../nfc_cli_dump_common_types.h"
NfcCommand nfc_cli_dump_poller_callback_iso14443_4a(NfcGenericEvent event, void* context);

View File

@@ -0,0 +1,25 @@
#include "nfc_cli_dump_iso14443_4b.h"
#include <nfc/protocols/iso14443_4b/iso14443_4b_poller.h>
NfcCommand nfc_cli_dump_poller_callback_iso14443_4b(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolIso14443_4b);
NfcCliDumpContext* instance = context;
const Iso14443_4bPollerEvent* iso14443_4b_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(iso14443_4b_event->type == Iso14443_4bPollerEventTypeReady) {
nfc_device_set_data(
instance->nfc_device, NfcProtocolIso14443_4b, nfc_poller_get_data(instance->poller));
command = NfcCommandStop;
} else if(iso14443_4b_event->type == Iso14443_4bPollerEventTypeError) {
instance->result = NfcCliDumpErrorFailedToRead;
command = NfcCommandStop;
}
if(command == NfcCommandStop) {
furi_semaphore_release(instance->sem_done);
}
return command;
}

View File

@@ -0,0 +1,5 @@
#pragma once
#include "../nfc_cli_dump_common_types.h"
NfcCommand nfc_cli_dump_poller_callback_iso14443_4b(NfcGenericEvent event, void* context);

View File

@@ -0,0 +1,26 @@
#include "nfc_cli_dump_iso15693_3.h"
#include <nfc/protocols/iso15693_3/iso15693_3_poller.h>
NfcCommand nfc_cli_dump_poller_callback_iso15693_3(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolIso15693_3);
NfcCliDumpContext* instance = context;
const Iso15693_3PollerEvent* iso15693_3_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(iso15693_3_event->type == Iso15693_3PollerEventTypeReady) {
nfc_device_set_data(
instance->nfc_device, NfcProtocolIso15693_3, nfc_poller_get_data(instance->poller));
instance->result = NfcCliDumpErrorFailedToRead;
command = NfcCommandStop;
} else if(iso15693_3_event->type == Iso15693_3PollerEventTypeError) {
instance->result = NfcCliDumpErrorFailedToRead;
command = NfcCommandStop;
}
if(command == NfcCommandStop) {
furi_semaphore_release(instance->sem_done);
}
return command;
}

View File

@@ -0,0 +1,5 @@
#pragma once
#include "../nfc_cli_dump_common_types.h"
NfcCommand nfc_cli_dump_poller_callback_iso15693_3(NfcGenericEvent event, void* context);

View File

@@ -0,0 +1,54 @@
#include "nfc_cli_dump_mf_classic.h"
#include <nfc/protocols/mf_classic/mf_classic_poller.h>
#define TAG "MFC"
NfcCommand nfc_cli_dump_poller_callback_mf_classic(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolMfClassic);
NfcCliDumpContext* instance = context;
const MfClassicPollerEvent* mfc_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(mfc_event->type == MfClassicPollerEventTypeRequestMode) {
nfc_device_set_data(
instance->nfc_device, NfcProtocolMfClassic, nfc_poller_get_data(instance->poller));
size_t uid_len = 0;
const uint8_t* uid = nfc_device_get_uid(instance->nfc_device, &uid_len);
if(mf_classic_key_cache_load(instance->mfc_key_cache, uid, uid_len)) {
FURI_LOG_D(TAG, "Key cache found");
mfc_event->data->poller_mode.mode = MfClassicPollerModeRead;
} else {
FURI_LOG_D(TAG, "Key cache not found");
instance->result = NfcCliDumpErrorFailedToRead;
command = NfcCommandStop;
}
} else if(mfc_event->type == MfClassicPollerEventTypeRequestReadSector) {
uint8_t sector_num = 0;
MfClassicKey key = {};
MfClassicKeyType key_type = MfClassicKeyTypeA;
if(mf_classic_key_cache_get_next_key(
instance->mfc_key_cache, &sector_num, &key, &key_type)) {
mfc_event->data->read_sector_request_data.sector_num = sector_num;
mfc_event->data->read_sector_request_data.key = key;
mfc_event->data->read_sector_request_data.key_type = key_type;
mfc_event->data->read_sector_request_data.key_provided = true;
} else {
mfc_event->data->read_sector_request_data.key_provided = false;
}
} else if(mfc_event->type == MfClassicPollerEventTypeSuccess) {
nfc_device_set_data(
instance->nfc_device, NfcProtocolMfClassic, nfc_poller_get_data(instance->poller));
instance->result = NfcCliDumpErrorNone;
command = NfcCommandStop;
} else if(mfc_event->type == MfClassicPollerEventTypeFail) {
instance->result = NfcCliDumpErrorFailedToRead;
command = NfcCommandStop;
}
if(command == NfcCommandStop) {
furi_semaphore_release(instance->sem_done);
}
return command;
}

View File

@@ -0,0 +1,5 @@
#pragma once
#include "../nfc_cli_dump_common_types.h"
NfcCommand nfc_cli_dump_poller_callback_mf_classic(NfcGenericEvent event, void* context);

View File

@@ -0,0 +1,27 @@
#include "nfc_cli_dump_mf_desfire.h"
#include <nfc/protocols/mf_desfire/mf_desfire_poller.h>
#define TAG "MFDES"
NfcCommand nfc_cli_dump_poller_callback_mf_desfire(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolMfDesfire);
NfcCommand command = NfcCommandContinue;
NfcCliDumpContext* instance = context;
const MfDesfirePollerEvent* mf_desfire_event = event.event_data;
if(mf_desfire_event->type == MfDesfirePollerEventTypeReadSuccess) {
nfc_device_set_data(
instance->nfc_device, NfcProtocolMfDesfire, nfc_poller_get_data(instance->poller));
instance->result = NfcCliDumpErrorNone;
furi_semaphore_release(instance->sem_done);
command = NfcCommandStop;
} else if(mf_desfire_event->type == MfDesfirePollerEventTypeReadFailed) {
instance->result = NfcCliDumpErrorFailedToRead;
command = NfcCommandReset;
furi_semaphore_release(instance->sem_done);
}
return command;
}

View File

@@ -0,0 +1,5 @@
#pragma once
#include "../nfc_cli_dump_common_types.h"
NfcCommand nfc_cli_dump_poller_callback_mf_desfire(NfcGenericEvent event, void* context);

View File

@@ -0,0 +1,31 @@
#include "nfc_cli_dump_mf_plus.h"
#include <nfc/protocols/mf_plus/mf_plus_poller.h>
#define TAG "MFPLUS"
NfcCommand nfc_cli_dump_poller_callback_mf_plus(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.protocol == NfcProtocolMfPlus);
furi_assert(event.event_data);
NfcCliDumpContext* instance = context;
const MfPlusPollerEvent* mf_plus_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(mf_plus_event->type == MfPlusPollerEventTypeReadSuccess) {
nfc_device_set_data(
instance->nfc_device, NfcProtocolMfPlus, nfc_poller_get_data(instance->poller));
instance->result = NfcCliDumpErrorNone;
command = NfcCommandStop;
} else if(mf_plus_event->type == MfPlusPollerEventTypeReadFailed) {
instance->result = NfcCliDumpErrorFailedToRead;
command = NfcCommandReset;
}
if(command == NfcCommandStop) {
furi_semaphore_release(instance->sem_done);
}
return command;
}

View File

@@ -0,0 +1,5 @@
#pragma once
#include "../nfc_cli_dump_common_types.h"
NfcCommand nfc_cli_dump_poller_callback_mf_plus(NfcGenericEvent event, void* context);

View File

@@ -0,0 +1,39 @@
#include "nfc_cli_dump_mf_ultralight.h"
#include <nfc/protocols/mf_ultralight/mf_ultralight_poller.h>
#define TAG "MFU"
NfcCommand nfc_cli_dump_poller_callback_mf_ultralight(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolMfUltralight);
NfcCliDumpContext* instance = context;
const MfUltralightPollerEvent* mf_ultralight_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(mf_ultralight_event->type == MfUltralightPollerEventTypeReadSuccess) {
nfc_device_set_data(
instance->nfc_device, NfcProtocolMfUltralight, nfc_poller_get_data(instance->poller));
instance->result = NfcCliDumpErrorNone;
command = NfcCommandStop;
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthRequest) {
nfc_device_set_data(
instance->nfc_device, NfcProtocolMfUltralight, nfc_poller_get_data(instance->poller));
const NfcCliDumpAuthContext* auth_ctx = &instance->auth_ctx;
mf_ultralight_event->data->auth_context.skip_auth = auth_ctx->skip_auth;
mf_ultralight_event->data->auth_context.password = auth_ctx->key.password;
mf_ultralight_event->data->auth_context.tdes_key = auth_ctx->key.tdes_key;
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthFailed) {
instance->result = NfcCliDumpErrorAuthFailed;
command = NfcCommandStop;
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeReadFailed) {
instance->result = NfcCliDumpErrorAuthFailed;
command = NfcCommandStop;
}
if(command == NfcCommandStop) {
furi_semaphore_release(instance->sem_done);
}
return command;
}

View File

@@ -0,0 +1,5 @@
#pragma once
#include "../nfc_cli_dump_common_types.h"
NfcCommand nfc_cli_dump_poller_callback_mf_ultralight(NfcGenericEvent event, void* context);

View File

@@ -0,0 +1,59 @@
#pragma once
#include <furi.h>
#include "../../../../helpers/mf_classic_key_cache.h"
#include "../../helpers/nfc_cli_scanner.h"
#include <nfc/nfc.h>
#include <nfc/protocols/nfc_protocol.h>
#include <nfc/nfc_device.h>
#include <nfc/nfc_poller.h>
#include <nfc/protocols/felica/felica.h>
#include <nfc/protocols/mf_ultralight/mf_ultralight.h>
#include <storage/storage.h>
#define NFC_CLI_DUMP_KEY_MAX_SIZE (16)
typedef union {
MfUltralightAuthPassword password;
FelicaCardKey felica_key;
MfUltralightC3DesAuthKey tdes_key;
uint8_t key[NFC_CLI_DUMP_KEY_MAX_SIZE];
} NfcCliDumpKeyUnion;
typedef struct {
NfcCliDumpKeyUnion key;
uint8_t key_size;
bool skip_auth;
} NfcCliDumpAuthContext;
typedef enum {
NfcCliDumpErrorNone,
NfcCliDumpErrorTimeout,
NfcCliDumpErrorNotPresent,
NfcCliDumpErrorFailedToRead,
NfcCliDumpErrorAuthFailed,
NfcCliDumpErrorNum,
} NfcCliDumpError;
typedef struct {
Nfc* nfc;
FuriString* file_path;
Storage* storage;
NfcCliScanner* scanner;
NfcProtocol desired_protocol;
uint32_t timeout;
FuriSemaphore* sem_done;
NfcCliDumpError result;
NfcCliDumpAuthContext auth_ctx;
MfClassicKeyCache* mfc_key_cache;
NfcPoller* poller;
NfcDevice* nfc_device;
} NfcCliDumpContext;

View File

@@ -0,0 +1,26 @@
#include "nfc_cli_dump_slix.h"
#include <nfc/protocols/slix/slix_poller.h>
NfcCommand nfc_cli_dump_poller_callback_slix(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolSlix);
NfcCliDumpContext* instance = context;
const SlixPollerEvent* slix_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(slix_event->type == SlixPollerEventTypeReady) {
nfc_device_set_data(
instance->nfc_device, NfcProtocolSlix, nfc_poller_get_data(instance->poller));
command = NfcCommandStop;
} else if(slix_event->type == SlixPollerEventTypeError) {
instance->result = NfcCliDumpErrorFailedToRead;
command = NfcCommandStop;
}
if(command == NfcCommandStop) {
furi_semaphore_release(instance->sem_done);
}
return command;
}

View File

@@ -0,0 +1,5 @@
#pragma once
#include "../nfc_cli_dump_common_types.h"
NfcCommand nfc_cli_dump_poller_callback_slix(NfcGenericEvent event, void* context);

View File

@@ -0,0 +1,29 @@
#include "nfc_cli_dump_st25tb.h"
#include <nfc/protocols/st25tb/st25tb_poller.h>
NfcCommand nfc_cli_dump_poller_callback_st25tb(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolSt25tb);
NfcCliDumpContext* instance = context;
const St25tbPollerEvent* st25tb_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(st25tb_event->type == St25tbPollerEventTypeRequestMode) {
st25tb_event->data->mode_request.mode = St25tbPollerModeRead;
} else if(st25tb_event->type == St25tbPollerEventTypeSuccess) {
nfc_device_set_data(
instance->nfc_device, NfcProtocolSt25tb, nfc_poller_get_data(instance->poller));
instance->result = NfcCliDumpErrorNone;
command = NfcCommandStop;
} else if(st25tb_event->type == St25tbPollerEventTypeFailure) {
instance->result = NfcCliDumpErrorFailedToRead;
command = NfcCommandStop;
}
if(command == NfcCommandStop) {
furi_semaphore_release(instance->sem_done);
}
return command;
}

View File

@@ -0,0 +1,5 @@
#pragma once
#include "../nfc_cli_dump_common_types.h"
NfcCommand nfc_cli_dump_poller_callback_st25tb(NfcGenericEvent event, void* context);

View File

@@ -0,0 +1,61 @@
#include "nfc_cli_format.h"
static const char* protocol_names[NfcProtocolNum] = {
[NfcProtocolIso14443_3a] = "Iso14443-3a",
[NfcProtocolIso14443_3b] = "Iso14443-3b",
[NfcProtocolIso14443_4a] = "Iso14443-4a",
[NfcProtocolIso14443_4b] = "Iso14443-4b",
[NfcProtocolIso15693_3] = "Iso15693-3",
[NfcProtocolFelica] = "FeliCa",
[NfcProtocolMfUltralight] = "Mifare Ultralight",
[NfcProtocolMfClassic] = "Mifare Classic",
[NfcProtocolMfDesfire] = "Mifare DESFire",
[NfcProtocolMfPlus] = "Mifare Plus",
[NfcProtocolSlix] = "Slix",
[NfcProtocolSt25tb] = "St25tb",
};
const char* nfc_cli_get_protocol_name(NfcProtocol protocol) {
furi_assert(protocol < NfcProtocolNum);
return protocol_names[protocol];
}
static const char* mf_ultralight_error_names[] = {
[MfUltralightErrorNone] = "OK",
[MfUltralightErrorNotPresent] = "Card not present",
[MfUltralightErrorProtocol] = "Protocol failure",
[MfUltralightErrorAuth] = "Auth failed",
[MfUltralightErrorTimeout] = "Timeout",
};
const char* nfc_cli_mf_ultralight_get_error(MfUltralightError error) {
furi_assert(error < COUNT_OF(mf_ultralight_error_names));
return mf_ultralight_error_names[error];
}
void nfc_cli_format_array(
const uint8_t* data,
const size_t data_size,
const char* header,
FuriString* output) {
furi_assert(data);
furi_assert(data_size > 0);
furi_assert(header);
furi_assert(output);
furi_string_cat_printf(output, "%s", header);
for(size_t i = 0; i < data_size; i++) {
furi_string_cat_printf(output, "%02X ", data[i]);
}
}
void nfc_cli_printf_array(const uint8_t* data, const size_t data_size, const char* header) {
furi_assert(data);
furi_assert(data_size > 0);
furi_assert(header);
printf("%s", header);
for(size_t i = 0; i < data_size; i++) {
printf("%02X ", data[i]);
}
}

View File

@@ -0,0 +1,16 @@
#pragma once
#include <furi.h>
#include <nfc/protocols/nfc_protocol.h>
#include <nfc/protocols/mf_ultralight/mf_ultralight.h>
const char* nfc_cli_get_protocol_name(NfcProtocol protocol);
const char* nfc_cli_mf_ultralight_get_error(MfUltralightError error);
void nfc_cli_format_array(
const uint8_t* data,
const size_t data_size,
const char* header,
FuriString* output);
void nfc_cli_printf_array(const uint8_t* data, const size_t data_size, const char* header);

View File

@@ -0,0 +1,58 @@
#include "nfc_cli_protocol_parser.h"
#include <m-bptree.h>
#define PROTOCOLS_TREE_RANK 4
BPTREE_DEF2(
ProtocolTree,
PROTOCOLS_TREE_RANK,
FuriString*,
FURI_STRING_OPLIST,
NfcProtocol,
M_POD_OPLIST);
#define M_OPL_ProtocolTree_t() BPTREE_OPLIST(ProtocolTree, M_POD_OPLIST)
struct NfcCliProtocolParser {
ProtocolTree_t protocols;
};
NfcCliProtocolParser* nfc_cli_protocol_parser_alloc(
const NfcProtocolNameValuePair* valid_protocols,
const size_t valid_count) {
furi_assert(valid_protocols);
furi_assert(valid_count > 0);
NfcCliProtocolParser* instance = malloc(sizeof(NfcCliProtocolParser));
FuriString* name = furi_string_alloc();
ProtocolTree_init(instance->protocols);
for(size_t i = 0; i < valid_count; i++) {
const NfcProtocolNameValuePair* item = &valid_protocols[i];
furi_string_set_str(name, item->name);
ProtocolTree_set_at(instance->protocols, name, item->value);
}
furi_string_free(name);
return instance;
}
void nfc_cli_protocol_parser_free(NfcCliProtocolParser* instance) {
furi_assert(instance);
ProtocolTree_clear(instance->protocols);
free(instance);
}
bool nfc_cli_protocol_parser_get(
NfcCliProtocolParser* instance,
FuriString* key,
NfcProtocol* result) {
furi_assert(instance);
furi_assert(key);
NfcProtocol* protocol = ProtocolTree_get(instance->protocols, key);
if(protocol) *result = *protocol;
return protocol != NULL;
}

View File

@@ -0,0 +1,21 @@
#pragma once
#include <furi.h>
#include <nfc/protocols/nfc_protocol.h>
typedef struct {
const char* name;
NfcProtocol value;
} NfcProtocolNameValuePair;
typedef struct NfcCliProtocolParser NfcCliProtocolParser;
NfcCliProtocolParser* nfc_cli_protocol_parser_alloc(
const NfcProtocolNameValuePair* valid_protocols,
const size_t valid_count);
void nfc_cli_protocol_parser_free(NfcCliProtocolParser* instance);
bool nfc_cli_protocol_parser_get(
NfcCliProtocolParser* instance,
FuriString* key,
NfcProtocol* result);

View File

@@ -0,0 +1,96 @@
#include "nfc_cli_scanner.h"
#include <nfc/nfc_scanner.h>
#include "nfc_cli_format.h"
#define NFC_CLI_SCANNER_FLAG_DETECTED (1UL << 0)
struct NfcCliScanner {
Nfc* nfc;
size_t protocols_detected_num;
NfcProtocol protocols_detected[NfcProtocolNum];
FuriThreadId thread_id;
NfcScanner* scanner;
};
NfcCliScanner* nfc_cli_scanner_alloc(Nfc* nfc) {
NfcCliScanner* instance = malloc(sizeof(NfcCliScanner));
instance->nfc = nfc;
instance->thread_id = furi_thread_get_current_id();
return instance;
}
void nfc_cli_scanner_free(NfcCliScanner* instance) {
furi_assert(instance);
free(instance);
}
static void nfc_cli_scanner_detect_callback(NfcScannerEvent event, void* context) {
furi_assert(context);
NfcCliScanner* instance = context;
if(event.type == NfcScannerEventTypeDetected) {
instance->protocols_detected_num = event.data.protocol_num;
memcpy(
instance->protocols_detected,
event.data.protocols,
event.data.protocol_num * sizeof(NfcProtocol));
furi_thread_flags_set(instance->thread_id, NFC_CLI_SCANNER_FLAG_DETECTED);
}
}
bool nfc_cli_scanner_detect_protocol(NfcCliScanner* instance, uint32_t timeout) {
instance->scanner = nfc_scanner_alloc(instance->nfc);
nfc_scanner_start(instance->scanner, nfc_cli_scanner_detect_callback, instance);
uint32_t event =
furi_thread_flags_wait(NFC_CLI_SCANNER_FLAG_DETECTED, FuriFlagWaitAny, timeout);
nfc_scanner_stop(instance->scanner);
nfc_scanner_free(instance->scanner);
return (event == NFC_CLI_SCANNER_FLAG_DETECTED);
}
void nfc_cli_scanner_begin_scan(NfcCliScanner* instance) {
instance->scanner = nfc_scanner_alloc(instance->nfc);
nfc_scanner_start(instance->scanner, nfc_cli_scanner_detect_callback, instance);
}
bool nfc_cli_scanner_wait_scan(NfcCliScanner* instance, uint32_t timeout) {
UNUSED(instance);
uint32_t event =
furi_thread_flags_wait(NFC_CLI_SCANNER_FLAG_DETECTED, FuriFlagWaitAny, timeout);
return (event == NFC_CLI_SCANNER_FLAG_DETECTED);
}
void nfc_cli_scanner_end_scan(NfcCliScanner* instance) {
nfc_scanner_stop(instance->scanner);
nfc_scanner_free(instance->scanner);
}
void nfc_cli_scanner_list_detected_protocols(NfcCliScanner* instance) {
printf("Protocols detected: ");
size_t n = instance->protocols_detected_num;
for(size_t i = 0; i < n; i++) {
const char* name = nfc_cli_get_protocol_name(instance->protocols_detected[i]);
printf((i == (n - 1)) ? "%s\r\n" : "%s, ", name);
}
}
bool nfc_cli_scanner_protocol_was_detected(NfcCliScanner* instance, NfcProtocol protocol) {
furi_assert(instance);
furi_assert(protocol < NfcProtocolNum);
for(size_t i = 0; i < instance->protocols_detected_num; i++) {
if(instance->protocols_detected[i] == protocol) return true;
}
return false;
}
NfcProtocol nfc_cli_scanner_get_protocol(NfcCliScanner* instance, size_t idx) {
furi_assert(instance);
furi_assert(idx < instance->protocols_detected_num);
return instance->protocols_detected[idx];
}
size_t nfc_cli_scanner_detected_protocol_num(NfcCliScanner* instance) {
furi_assert(instance);
return instance->protocols_detected_num;
}

View File

@@ -0,0 +1,21 @@
#pragma once
#include <furi.h>
#include <nfc.h>
#include <nfc/protocols/nfc_protocol.h>
typedef struct NfcCliScanner NfcCliScanner;
NfcCliScanner* nfc_cli_scanner_alloc(Nfc* nfc);
void nfc_cli_scanner_free(NfcCliScanner* instance);
bool nfc_cli_scanner_detect_protocol(NfcCliScanner* instance, uint32_t timeout);
void nfc_cli_scanner_begin_scan(NfcCliScanner* instance);
bool nfc_cli_scanner_wait_scan(NfcCliScanner* instance, uint32_t timeout);
void nfc_cli_scanner_end_scan(NfcCliScanner* instance);
void nfc_cli_scanner_list_detected_protocols(NfcCliScanner* instance);
size_t nfc_cli_scanner_detected_protocol_num(NfcCliScanner* instance);
bool nfc_cli_scanner_protocol_was_detected(NfcCliScanner* instance, NfcProtocol protocol);
NfcProtocol nfc_cli_scanner_get_protocol(NfcCliScanner* instance, size_t idx);

View File

@@ -0,0 +1,227 @@
#include "nfc_cli_action_info.h"
#include "../../../helpers/protocol_support/mf_ultralight/mf_ultralight_render.h"
#include "../helpers/nfc_cli_format.h"
#include <furi.h>
#include <flipper_format/flipper_format.h>
#include <storage/storage.h>
#include <nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h>
#define TAG "INFO"
typedef struct {
uint8_t magic;
union {
uint8_t value;
struct {
uint8_t minor : 4;
uint8_t major : 4;
};
} version;
uint8_t size;
union {
uint8_t value;
struct {
uint8_t write : 4;
uint8_t read : 4;
};
} access;
} FURI_PACKED MfUltralightCapabilityContainer;
typedef struct {
Nfc* nfc;
MfUltralightData* data;
} NfcCliMfuContext;
static void nfc_cli_mfu_info_get_vendor(const uint8_t vendor_key, FuriString* output) {
furi_assert(output);
FuriString* buf = furi_string_alloc();
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* ff = flipper_format_file_alloc(storage);
do {
if(!flipper_format_file_open_existing(ff, EXT_PATH("nfc/assets/vendors.nfc"))) {
FURI_LOG_W(TAG, "NFC Vendors dict not found");
break;
}
char uid_str[5];
snprintf(uid_str, sizeof(uid_str), "%d", vendor_key);
if(flipper_format_read_string(ff, uid_str, buf))
furi_string_printf(output, "%s, %s", uid_str, furi_string_get_cstr(buf));
else
furi_string_printf(output, "unknown");
} while(false);
flipper_format_free(ff);
furi_record_close(RECORD_STORAGE);
furi_string_free(buf);
}
const char*
nfc_cli_mfu_capability_container_get_access_description(const uint8_t value, bool read) {
const char* description = "RFU"; //value 0x01 - 0x07, and 0xF when read
if(value == 0x00)
description = "access fully granted";
else if(value >= 0x08 && value <= 0x0E)
description = "proprietary";
else if(value == 0x0F && !read)
description = "no access granted at all";
return description;
}
static void nfc_cli_mfu_info_print_common(const MfUltralightData* data) {
FuriString* str = furi_string_alloc();
printf(ANSI_FG_GREEN "\r\n\tTag information\r\n" ANSI_RESET);
printf(
"Type: " ANSI_FG_YELLOW "%s\r\n" ANSI_RESET,
mf_ultralight_get_device_name(data, NfcDeviceNameTypeFull));
nfc_cli_mfu_info_get_vendor(data->iso14443_3a_data->uid[0], str);
printf("Vendor ID: %s\r\n", furi_string_get_cstr(str));
furi_string_reset(str);
nfc_render_mf_ultralight_info(data, NfcProtocolFormatTypeFull, str);
printf("%s\r\n", furi_string_get_cstr(str));
printf("BCC0: %02X\r\nBCC1: %02X\r\n", data->page[0].data[3], data->page[2].data[0]);
furi_string_free(str);
}
static void nfc_cli_mfu_info_print_ndef(const MfUltralightData* data) {
const MfUltralightCapabilityContainer* cc =
(const MfUltralightCapabilityContainer*)data->page[3].data;
if(cc->magic == 0xE1) {
printf(ANSI_FG_GREEN "\r\n\tNDEF Message\r\n" ANSI_RESET);
nfc_cli_printf_array(data->page[3].data, 4, "Capability container: ");
printf(
"\r\nMagic number: %02X\r\nVersion %d.%d\r\nSize: [%02X] - %d bytes\r\n",
cc->magic,
cc->version.major,
cc->version.minor,
cc->size,
cc->size * 8);
printf(
"Access read: [%02X] - %s",
cc->access.read,
nfc_cli_mfu_capability_container_get_access_description(cc->access.read, true));
printf(
"Access write: [%02X] - %s",
cc->access.write,
nfc_cli_mfu_capability_container_get_access_description(cc->access.write, false));
}
}
static void nfc_cli_mfu_info_print_counter(const MfUltralightData* data) {
uint32_t features = mf_ultralight_get_feature_support_set(data->type);
if(!mf_ultralight_support_feature(features, MfUltralightFeatureSupportReadCounter)) return;
printf(ANSI_FG_GREEN "\r\n\n\tTag counters\r\n" ANSI_RESET);
uint8_t i =
mf_ultralight_support_feature(features, MfUltralightFeatureSupportSingleCounter) ? 2 : 0;
for(; i < MF_ULTRALIGHT_COUNTER_NUM; i++) {
printf("Counter [%d]: ", i);
nfc_cli_printf_array(data->counter[i].data, MF_ULTRALIGHT_COUNTER_SIZE, "");
printf(" Value: %lu\r\n", data->counter[i].counter);
const uint8_t tf = data->tearing_flag[i].data;
printf(
"Tearing [%d]: [%02X] %s",
i,
tf,
tf == MF_ULTRALIGHT_TEARING_FLAG_DEFAULT ? "(ok)" : "");
}
}
static void nfc_cli_mfu_info_print_signature(const MfUltralightData* data) {
uint32_t features = mf_ultralight_get_feature_support_set(data->type);
if(!mf_ultralight_support_feature(features, MfUltralightFeatureSupportReadSignature)) return;
const MfUltralightSignature* signature = &data->signature;
printf(ANSI_FG_GREEN "\r\n\n\tTag signature\r\n" ANSI_RESET);
nfc_cli_printf_array(signature->data, sizeof(signature->data), "ECC signature: ");
}
static void nfc_cli_mfu_info_print_version_storage_size(uint8_t storage_size) {
uint16_t max_size = 1 << ((storage_size >> 1) + 1);
uint16_t min_exact_size = 1 << (storage_size >> 1);
bool exact_size = !(storage_size & 0x01);
if(exact_size)
printf("[%02X], (%u bytes)", storage_size, min_exact_size);
else
printf("[%02X], (%u <-> %u bytes)", storage_size, min_exact_size, max_size);
}
static void nfc_cli_mfu_info_print_version(const MfUltralightData* data) {
uint32_t features = mf_ultralight_get_feature_support_set(data->type);
if(!mf_ultralight_support_feature(features, MfUltralightFeatureSupportReadVersion)) return;
const MfUltralightVersion* version = &data->version;
printf(ANSI_FG_GREEN "\r\n\n\tTag Version\r\n" ANSI_RESET);
nfc_cli_printf_array((uint8_t*)version, sizeof(MfUltralightVersion), "Raw bytes: ");
FuriString* str = furi_string_alloc();
nfc_cli_mfu_info_get_vendor(version->vendor_id, str);
printf("\r\nVendor ID: %s\r\n", furi_string_get_cstr(str));
furi_string_free(str);
printf("Product type: %02X\r\n", version->prod_type);
printf(
"Protocol type: %02X%s\r\n",
version->protocol_type,
(version->protocol_type == 0x3) ? ", ISO14443-3 Compliant" : "");
printf(
"Product subtype: [%02X], %s\r\n",
version->prod_subtype,
(version->prod_subtype == 1) ? "17 pF" : "50pF");
printf(
"Major version: %02X\r\nMinor version: %02X\r\nSize: ",
version->prod_ver_major,
version->prod_ver_minor);
nfc_cli_mfu_info_print_version_storage_size(version->storage_size);
}
NfcCliActionContext* nfc_cli_mfu_info_alloc_ctx(Nfc* nfc) {
NfcCliMfuContext* instance = malloc(sizeof(NfcCliMfuContext));
instance->nfc = nfc;
instance->data = mf_ultralight_alloc();
return instance;
}
void nfc_cli_mfu_info_free_ctx(NfcCliActionContext* ctx) {
NfcCliMfuContext* instance = ctx;
mf_ultralight_free(instance->data);
free(instance);
}
void nfc_cli_mfu_info_execute(PipeSide* pipe, NfcCliActionContext* ctx) {
furi_assert(pipe);
furi_assert(ctx);
NfcCliMfuContext* instance = ctx;
MfUltralightError error =
mf_ultralight_poller_sync_read_card(instance->nfc, instance->data, NULL);
if(error == MfUltralightErrorNone) {
const MfUltralightData* data = instance->data;
nfc_cli_mfu_info_print_common(data);
nfc_cli_mfu_info_print_ndef(data);
nfc_cli_mfu_info_print_counter(data);
nfc_cli_mfu_info_print_signature(data);
nfc_cli_mfu_info_print_version(data);
} else {
printf(ANSI_FG_RED "Error: %s" ANSI_RESET, nfc_cli_mf_ultralight_get_error(error));
}
}

View File

@@ -0,0 +1,7 @@
#pragma once
#include "../../nfc_cli_command_base_i.h"
NfcCliActionContext* nfc_cli_mfu_info_alloc_ctx(Nfc* nfc);
void nfc_cli_mfu_info_free_ctx(NfcCliActionContext* ctx);
void nfc_cli_mfu_info_execute(PipeSide* pipe, NfcCliActionContext* ctx);

View File

@@ -0,0 +1,52 @@
#include "nfc_cli_action_rdbl.h"
#include "../helpers/nfc_cli_format.h"
#include <furi.h>
#include <toolbox/strint.h>
#include <nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h>
#define MF_ULTRALIGHT_POLLER_COMPLETE_EVENT (1UL << 0)
typedef struct {
Nfc* nfc;
uint16_t block;
} NfcCliMfuRdblContext;
NfcCliActionContext* nfc_cli_mfu_rdbl_alloc_ctx(Nfc* nfc) {
furi_assert(nfc);
NfcCliMfuRdblContext* instance = malloc(sizeof(NfcCliMfuRdblContext));
instance->nfc = nfc;
return instance;
}
void nfc_cli_mfu_rdbl_free_ctx(NfcCliActionContext* ctx) {
furi_assert(ctx);
NfcCliMfuRdblContext* instance = ctx;
free(instance);
}
void nfc_cli_mfu_rdbl_execute(PipeSide* pipe, NfcCliActionContext* ctx) {
furi_assert(pipe);
NfcCliMfuRdblContext* instance = ctx;
MfUltralightPage page = {0};
MfUltralightError error =
mf_ultralight_poller_sync_read_page(instance->nfc, instance->block, &page);
if(error == MfUltralightErrorNone) {
printf("\r\nBlock: %d ", instance->block);
nfc_cli_printf_array(page.data, sizeof(MfUltralightPage), "Data: ");
printf("\r\n");
} else {
printf(ANSI_FG_RED "Error: %s" ANSI_RESET, nfc_cli_mf_ultralight_get_error(error));
}
}
bool nfc_cli_mfu_rdbl_parse_block(FuriString* value, NfcCliActionContext* output) {
NfcCliMfuRdblContext* ctx = output;
StrintParseError err = strint_to_uint16(furi_string_get_cstr(value), NULL, &ctx->block, 10);
return err == StrintParseNoError;
}

View File

@@ -0,0 +1,8 @@
#pragma once
#include "../../nfc_cli_command_base_i.h"
NfcCliActionContext* nfc_cli_mfu_rdbl_alloc_ctx(Nfc* nfc);
void nfc_cli_mfu_rdbl_free_ctx(NfcCliActionContext* ctx);
void nfc_cli_mfu_rdbl_execute(PipeSide* pipe, NfcCliActionContext* ctx);
bool nfc_cli_mfu_rdbl_parse_block(FuriString* value, NfcCliActionContext* ctx);

View File

@@ -0,0 +1,71 @@
#include "nfc_cli_action_rdbl.h"
#include "../helpers/nfc_cli_format.h"
#include <furi.h>
#include <toolbox/args.h>
#include <toolbox/strint.h>
#include <nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h>
#define MF_ULTRALIGHT_POLLER_COMPLETE_EVENT (1UL << 0)
typedef struct {
Nfc* nfc;
uint16_t block;
MfUltralightPage page;
} NfcCliMfuWrblContext;
NfcCliActionContext* nfc_cli_mfu_wrbl_alloc_ctx(Nfc* nfc) {
furi_assert(nfc);
NfcCliMfuWrblContext* instance = malloc(sizeof(NfcCliMfuWrblContext));
instance->nfc = nfc;
return instance;
}
void nfc_cli_mfu_wrbl_free_ctx(NfcCliActionContext* ctx) {
furi_assert(ctx);
NfcCliMfuWrblContext* instance = ctx;
free(instance);
}
void nfc_cli_mfu_wrbl_execute(PipeSide* pipe, NfcCliActionContext* ctx) {
furi_assert(pipe);
NfcCliMfuWrblContext* instance = ctx;
MfUltralightError error =
mf_ultralight_poller_sync_write_page(instance->nfc, instance->block, &instance->page);
if(error == MfUltralightErrorNone) {
printf(ANSI_FG_BR_GREEN "\r\nSuccess\r\n" ANSI_RESET);
printf("Block: %d ", instance->block);
nfc_cli_printf_array(instance->page.data, sizeof(MfUltralightPage), "Data: ");
printf("\r\n");
} else {
printf(ANSI_FG_RED "Error: %s" ANSI_RESET, nfc_cli_mf_ultralight_get_error(error));
}
}
bool nfc_cli_mfu_wrbl_parse_block(FuriString* value, NfcCliActionContext* output) {
NfcCliMfuWrblContext* ctx = output;
StrintParseError err = strint_to_uint16(furi_string_get_cstr(value), NULL, &ctx->block, 10);
return err == StrintParseNoError;
}
bool nfc_cli_mfu_wrbl_parse_data(FuriString* value, void* output) {
NfcCliMfuWrblContext* ctx = output;
bool result = false;
do {
size_t len = furi_string_size(value);
if(len % 2 != 0) break;
size_t data_length = len / 2;
if(data_length != MF_ULTRALIGHT_PAGE_SIZE) break;
result = args_read_hex_bytes(value, ctx->page.data, data_length);
} while(false);
return result;
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include "../../nfc_cli_command_base_i.h"
NfcCliActionContext* nfc_cli_mfu_wrbl_alloc_ctx(Nfc* nfc);
void nfc_cli_mfu_wrbl_free_ctx(NfcCliActionContext* ctx);
void nfc_cli_mfu_wrbl_execute(PipeSide* pipe, NfcCliActionContext* ctx);
bool nfc_cli_mfu_wrbl_parse_block(FuriString* value, NfcCliActionContext* ctx);
bool nfc_cli_mfu_wrbl_parse_data(FuriString* value, void* output);

View File

@@ -0,0 +1,77 @@
#include "nfc_cli_command_mfu.h"
#include "nfc_cli_action_info.h"
#include "nfc_cli_action_rdbl.h"
#include "nfc_cli_action_wrbl.h"
#define TAG "MFU"
//mfu info
const NfcCliActionDescriptor info_action = {
.name = "info",
.description = "Get basic information about the card",
.alloc = nfc_cli_mfu_info_alloc_ctx,
.free = nfc_cli_mfu_info_free_ctx,
.execute = nfc_cli_mfu_info_execute,
.key_count = 0,
.keys = NULL,
};
const NfcCliKeyDescriptor rdbl_action_keys[] = {
{
.short_name = "b",
.long_name = "block",
.features = {.required = true, .parameter = true},
.description = "desired block number",
.parse = nfc_cli_mfu_rdbl_parse_block,
},
};
//mfu rdbl -b 0
//mfu rdbl --block 0
const NfcCliActionDescriptor rdbl_action = {
.name = "rdbl",
.description = "Read block from ultralight card",
.alloc = nfc_cli_mfu_rdbl_alloc_ctx,
.free = nfc_cli_mfu_rdbl_free_ctx,
.execute = nfc_cli_mfu_rdbl_execute,
.key_count = COUNT_OF(rdbl_action_keys),
.keys = rdbl_action_keys,
};
const NfcCliKeyDescriptor wrbl_action_keys[] = {
{
.short_name = "b",
.long_name = "block",
.features = {.required = true, .parameter = true},
.description = "desired block number",
.parse = nfc_cli_mfu_wrbl_parse_block,
},
{
.short_name = "d",
.long_name = "data",
.features = {.required = true, .parameter = true},
.description = "new data for block",
.parse = nfc_cli_mfu_wrbl_parse_data,
},
};
//mfu wrbl -b 0 -d DEADBEAF
//mfu rdbl --block 0 -- data DEADBEEF
const NfcCliActionDescriptor wrbl_action = {
.name = "wrbl",
.description = "Read block from ultralight card",
.alloc = nfc_cli_mfu_wrbl_alloc_ctx,
.free = nfc_cli_mfu_wrbl_free_ctx,
.execute = nfc_cli_mfu_wrbl_execute,
.key_count = COUNT_OF(wrbl_action_keys),
.keys = wrbl_action_keys,
};
const NfcCliActionDescriptor* mfu_actions[] = {
&rdbl_action,
&info_action,
&wrbl_action,
};
//Command descriptor
ADD_NFC_CLI_COMMAND(mfu, "Mifare Ultralight specific commands", mfu_actions);

View File

@@ -0,0 +1,5 @@
#pragma once
#include "../../nfc_cli_command_base_i.h"
extern const NfcCliCommandDescriptor mfu_cmd;

View File

@@ -0,0 +1,126 @@
#include "nfc_cli_command_emulate.h"
#include "helpers/nfc_cli_format.h"
#include <nfc.h>
#include <nfc_listener.h>
#include <nfc_device.h>
#include <storage/storage.h>
typedef struct {
Nfc* nfc;
NfcDevice* nfc_device;
FuriString* file_path;
Storage* storage;
} NfcCliEmulateContext;
static NfcCliActionContext* nfc_cli_emulate_alloc_ctx(Nfc* nfc) {
furi_assert(nfc);
NfcCliEmulateContext* instance = malloc(sizeof(NfcCliEmulateContext));
instance->nfc = nfc;
instance->file_path = furi_string_alloc();
instance->nfc_device = nfc_device_alloc();
instance->storage = furi_record_open(RECORD_STORAGE);
return instance;
}
static void nfc_cli_emulate_free_ctx(NfcCliActionContext* ctx) {
furi_assert(ctx);
NfcCliEmulateContext* instance = ctx;
furi_record_close(RECORD_STORAGE);
furi_string_free(instance->file_path);
nfc_device_free(instance->nfc_device);
free(instance);
}
static const NfcProtocol supported_protocols[] = {
NfcProtocolIso14443_3a,
NfcProtocolIso14443_4a,
NfcProtocolIso15693_3,
NfcProtocolMfUltralight,
NfcProtocolMfClassic,
NfcProtocolSlix,
NfcProtocolFelica,
};
static bool nfc_cli_emulate_protocol_supports_emulation(NfcProtocol protocol) {
for(size_t i = 0; i < COUNT_OF(supported_protocols); i++) {
if(supported_protocols[i] == protocol) return true;
}
return false;
}
static void nfc_cli_emulate_execute(PipeSide* pipe, NfcCliActionContext* context) {
UNUSED(pipe);
furi_assert(context);
NfcCliEmulateContext* instance = context;
do {
const char* path = furi_string_get_cstr(instance->file_path);
if(!storage_common_exists(instance->storage, path)) {
printf(ANSI_FG_RED "Wrong path \'%s\'.\r\n" ANSI_RESET, path);
break;
}
if(!nfc_device_load(instance->nfc_device, path)) {
printf(ANSI_FG_RED "Failed to load \'%s\'.\r\n" ANSI_RESET, path);
break;
}
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
if(!nfc_cli_emulate_protocol_supports_emulation(protocol)) {
printf(
ANSI_FG_RED "Error. Emulation for %s is not supported\r\n" ANSI_RESET,
nfc_cli_get_protocol_name(protocol));
break;
}
const NfcDeviceData* data = nfc_device_get_data(instance->nfc_device, protocol);
NfcListener* listener = nfc_listener_alloc(instance->nfc, protocol, data);
nfc_listener_start(listener, NULL, NULL);
printf("\r\nEmulating. Press Ctrl+C to abort\r\n");
while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
furi_delay_ms(100);
}
nfc_listener_stop(listener);
nfc_listener_free(listener);
} while(false);
}
static bool nfc_cli_emulate_parse_filename_key(FuriString* value, void* output) {
furi_assert(value);
furi_assert(output);
NfcCliEmulateContext* ctx = output;
furi_string_set(ctx->file_path, value);
return true;
}
const NfcCliKeyDescriptor emulate_keys[] = {
{
.features = {.required = true, .parameter = true},
.long_name = "file",
.short_name = "f",
.description = "path to new file",
.parse = nfc_cli_emulate_parse_filename_key,
},
};
const NfcCliActionDescriptor emulate_action = {
.name = "emulate",
.description = "Emulate .nfc file content",
.alloc = nfc_cli_emulate_alloc_ctx,
.free = nfc_cli_emulate_free_ctx,
.execute = nfc_cli_emulate_execute,
.key_count = COUNT_OF(emulate_keys),
.keys = emulate_keys,
};
const NfcCliActionDescriptor* emulate_actions_collection[] = {&emulate_action};
//Command descriptor
ADD_NFC_CLI_COMMAND(emulate, "", emulate_actions_collection);
//Command usage: emulate [-f <file>]
//Command examples:
//emulate -f ext/nfc/test.nfc

View File

@@ -0,0 +1,5 @@
#pragma once
#include "../nfc_cli_command_base_i.h"
extern const NfcCliCommandDescriptor emulate_cmd;

View File

@@ -0,0 +1,28 @@
#include "nfc_cli_command_field.h"
#include <furi_hal_nfc.h>
static void nfc_cli_field(PipeSide* pipe, FuriString* args, void* context) {
UNUSED(args);
UNUSED(context);
furi_hal_nfc_low_power_mode_stop();
furi_hal_nfc_poller_field_on();
printf("Field is on. Don't leave device in this mode for too long.\r\n");
printf("Press Ctrl+C to abort\r\n");
while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
furi_delay_ms(50);
}
furi_hal_nfc_low_power_mode_start();
}
const NfcCliCommandDescriptor field_cmd = {
.name = "field",
.description = "Turns NFC field on",
.callback = nfc_cli_field,
.action_count = 0,
.actions = NULL,
};

View File

@@ -0,0 +1,5 @@
#pragma once
#include "../nfc_cli_command_base_i.h"
extern const NfcCliCommandDescriptor field_cmd;

View File

@@ -0,0 +1,97 @@
#include "nfc_cli_command_scanner.h"
#include "helpers/nfc_cli_scanner.h"
#include "helpers/nfc_cli_format.h"
typedef struct {
NfcCliScanner* scanner;
bool display_tree;
} NfcCliCmdScannerContext;
static NfcCliActionContext* nfc_cli_command_scanner_alloc_ctx(Nfc* nfc) {
furi_assert(nfc);
NfcCliCmdScannerContext* instance = malloc(sizeof(NfcCliCmdScannerContext));
instance->scanner = nfc_cli_scanner_alloc(nfc);
instance->display_tree = false;
return instance;
}
static void nfc_cli_command_scanner_free_ctx(NfcCliActionContext* ctx) {
furi_assert(ctx);
NfcCliCmdScannerContext* instance = ctx;
nfc_cli_scanner_free(instance->scanner);
free(instance);
}
static void
nfc_cli_command_scanner_format_protocol_tree(NfcProtocol protocol, FuriString* output) {
const char* names[10] = {0};
uint8_t cnt = 0;
while(protocol != NfcProtocolInvalid) {
names[cnt++] = nfc_cli_get_protocol_name(protocol);
protocol = nfc_protocol_get_parent(protocol);
}
for(int8_t i = cnt - 1; i >= 0; i--) {
furi_string_cat_printf(output, (i == 0) ? "%s" : "%s -> ", names[i]);
}
}
static void nfc_cli_command_scanner_format_detected_protocols(NfcCliScanner* instance) {
FuriString* str = furi_string_alloc();
printf("Protocols detected: \r\n");
for(size_t i = 0; i < nfc_cli_scanner_detected_protocol_num(instance); i++) {
furi_string_reset(str);
NfcProtocol protocol = nfc_cli_scanner_get_protocol(instance, i);
nfc_cli_command_scanner_format_protocol_tree(protocol, str);
printf("Protocol [%zu]: %s\r\n", i + 1, furi_string_get_cstr(str));
}
furi_string_free(str);
}
static void nfc_cli_command_scanner_execute(PipeSide* pipe, void* context) {
NfcCliCmdScannerContext* instance = context;
printf("Press Ctrl+C to abort\r\n\n");
nfc_cli_scanner_begin_scan(instance->scanner);
while(!cli_is_pipe_broken_or_is_etx_next_char(pipe) &&
!nfc_cli_scanner_wait_scan(instance->scanner, 50))
;
nfc_cli_scanner_end_scan(instance->scanner);
if(!instance->display_tree)
nfc_cli_scanner_list_detected_protocols(instance->scanner);
else
nfc_cli_command_scanner_format_detected_protocols(instance->scanner);
}
static bool nfc_cli_command_scanner_parse_tree(FuriString* value, void* output) {
UNUSED(value);
NfcCliCmdScannerContext* ctx = output;
ctx->display_tree = true;
return true;
}
const NfcCliKeyDescriptor tree_key = {
.short_name = "t",
.long_name = "tree",
.features = {.parameter = false, .required = false, .multivalue = false},
.description = "displays protocol hierarchy for each detected protocol",
.parse = nfc_cli_command_scanner_parse_tree,
};
const NfcCliActionDescriptor scanner_action = {
.name = "scanner",
.description = "Detect tag type",
.key_count = 1,
.keys = &tree_key,
.execute = nfc_cli_command_scanner_execute,
.alloc = nfc_cli_command_scanner_alloc_ctx,
.free = nfc_cli_command_scanner_free_ctx,
};
const NfcCliActionDescriptor* scanner_actions_collection[] = {&scanner_action};
ADD_NFC_CLI_COMMAND(scanner, "", scanner_actions_collection);

View File

@@ -0,0 +1,5 @@
#pragma once
#include "../nfc_cli_command_base_i.h"
extern const NfcCliCommandDescriptor scanner_cmd;

View File

@@ -0,0 +1,352 @@
#include "nfc_cli_command_raw.h"
#include "../helpers/nfc_cli_format.h"
#include "../helpers/nfc_cli_protocol_parser.h"
#include "protocol_handlers/nfc_cli_raw_common_types.h"
#include "protocol_handlers/iso14443_3a/nfc_cli_raw_iso14443_3a.h"
#include "protocol_handlers/iso14443_3b/nfc_cli_raw_iso14443_3b.h"
#include "protocol_handlers/iso15693_3/nfc_cli_raw_iso15693_3.h"
#include "protocol_handlers/felica/nfc_cli_raw_felica.h"
#include <toolbox/args.h>
#define NFC_CLI_PROTOCOL_SUPPORT_MAX_BUFFER_SIZE (256)
#define TAG "RAW"
typedef enum {
NfcCliProtocolRequestTypeNormalExecute,
NfcCliProtocolRequestTypeAbort,
} NfcCliProtocolRequestType;
typedef enum {
NfcPollerStateStopped,
NfcPollerStateStarted,
} NfcPollerState;
typedef NfcCommand (*NfcCliRawProtocolSpecificHandler)(
NfcGenericInstance* poller,
const NfcCliRawRequest* request,
NfcCliRawResponse* const response);
typedef struct {
Nfc* nfc;
NfcCliRawRequest request;
NfcCliRawResponse response;
NfcPoller* poller;
NfcPollerState poller_state;
NfcCliProtocolRequestType request_type;
FuriMessageQueue* input_queue;
FuriSemaphore* sem_done;
} NfcCliRawCmdContext;
static const char* raw_error_names[] = {
[NfcCliRawErrorNone] = "None",
[NfcCliRawErrorTimeout] = "Timeout",
[NfcCliRawErrorProtocol] = "Internal protocol",
[NfcCliRawErrorWrongCrc] = "Wrong CRC",
[NfcCliRawErrorNotPresent] = "No card",
};
static NfcCliActionContext* nfc_cli_raw_alloc_ctx(Nfc* nfc) {
furi_assert(nfc);
NfcCliRawCmdContext* instance = malloc(sizeof(NfcCliRawCmdContext));
instance->nfc = nfc;
instance->request.protocol = NfcProtocolInvalid;
instance->request.tx_buffer = bit_buffer_alloc(NFC_CLI_PROTOCOL_SUPPORT_MAX_BUFFER_SIZE);
instance->response.rx_buffer = bit_buffer_alloc(NFC_CLI_PROTOCOL_SUPPORT_MAX_BUFFER_SIZE);
instance->input_queue = furi_message_queue_alloc(5, sizeof(NfcCliProtocolRequestType));
instance->sem_done = furi_semaphore_alloc(1, 0);
instance->response.activation_string = furi_string_alloc();
instance->request.timeout = 0;
return instance;
}
static void nfc_cli_raw_abort_nfc_thread(NfcCliRawCmdContext* instance) {
if(instance->poller_state == NfcPollerStateStarted) {
instance->request_type = NfcCliProtocolRequestTypeAbort;
furi_message_queue_put(instance->input_queue, &instance->request_type, FuriWaitForever);
furi_semaphore_acquire(instance->sem_done, FuriWaitForever);
instance->poller_state = NfcPollerStateStopped;
}
if(instance->poller) nfc_poller_stop(instance->poller);
}
static void nfc_cli_raw_free_ctx(NfcCliActionContext* ctx) {
furi_assert(ctx);
NfcCliRawCmdContext* instance = ctx;
nfc_cli_raw_abort_nfc_thread(instance);
if(instance->poller) nfc_poller_free(instance->poller);
furi_message_queue_free(instance->input_queue);
furi_semaphore_free(instance->sem_done);
furi_string_free(instance->response.activation_string);
bit_buffer_free(instance->response.rx_buffer);
bit_buffer_free(instance->request.tx_buffer);
instance->nfc = NULL;
free(instance);
}
static bool nfc_cli_raw_can_reuse_ctx(NfcCliActionContext* ctx) {
furi_assert(ctx);
NfcCliRawCmdContext* instance = ctx;
NfcCliRawRequest* request = &instance->request;
bool result = request->keep_field;
request->keep_field = false;
request->append_crc = false;
request->select = false;
instance->request.timeout = 0;
return result;
}
const NfcCliRawProtocolSpecificHandler nfc_cli_raw_protocol_handlers[] = {
[NfcProtocolIso14443_3a] = nfc_cli_raw_iso14443_3a_handler,
[NfcProtocolIso14443_3b] = nfc_cli_raw_iso14443_3b_handler,
[NfcProtocolIso14443_4a] = NULL,
[NfcProtocolIso14443_4b] = NULL,
[NfcProtocolIso15693_3] = nfc_cli_raw_iso15693_3_handler,
[NfcProtocolFelica] = nfc_cli_raw_felica_handler,
[NfcProtocolMfUltralight] = NULL,
[NfcProtocolMfClassic] = NULL,
[NfcProtocolMfDesfire] = NULL,
[NfcProtocolSlix] = NULL,
[NfcProtocolSt25tb] = NULL,
};
static NfcCommand nfc_cli_raw_poller_callback(NfcGenericEventEx event, void* context) {
NfcEvent* nfc_event = event.parent_event_data;
NfcCliRawCmdContext* instance = context;
NfcCommand command = NfcCommandContinue;
if(nfc_event->type == NfcEventTypePollerReady) {
FURI_LOG_D(TAG, "Poller callback");
NfcCliProtocolRequestType request_type = NfcCliProtocolRequestTypeAbort;
furi_message_queue_get(instance->input_queue, &request_type, FuriWaitForever);
if(request_type == NfcCliProtocolRequestTypeAbort) {
command = NfcCommandStop;
} else {
const NfcCliRawProtocolSpecificHandler handler =
nfc_cli_raw_protocol_handlers[instance->request.protocol];
if(handler) handler(event.poller, &instance->request, &instance->response);
}
}
furi_semaphore_release(instance->sem_done);
if(command == NfcCommandStop) {
FURI_LOG_D(TAG, "Aborting poller callback");
instance->poller_state = NfcPollerStateStopped;
}
return command;
}
static inline void nfc_cli_raw_print_result(const NfcCliRawCmdContext* instance) {
if(!furi_string_empty(instance->response.activation_string))
printf("%s\r\n", furi_string_get_cstr(instance->response.activation_string));
nfc_cli_printf_array(
bit_buffer_get_data(instance->request.tx_buffer),
bit_buffer_get_size_bytes(instance->request.tx_buffer),
"Tx: ");
if(instance->response.result != NfcCliRawErrorNone)
printf("\r\nError: \"%s\"\r\n", raw_error_names[instance->response.result]);
size_t rx_size = bit_buffer_get_size_bytes(instance->response.rx_buffer);
if(rx_size > 0) {
nfc_cli_printf_array(
bit_buffer_get_data(instance->response.rx_buffer),
bit_buffer_get_size_bytes(instance->response.rx_buffer),
"\r\nRx: ");
}
}
static void nfc_cli_raw_execute(PipeSide* pipe, void* context) {
UNUSED(pipe);
furi_assert(context);
NfcCliRawCmdContext* instance = context;
furi_string_reset(instance->response.activation_string);
if(instance->poller_state == NfcPollerStateStopped) {
if(instance->poller == NULL)
instance->poller = nfc_poller_alloc(instance->nfc, instance->request.protocol);
nfc_poller_start_ex(instance->poller, nfc_cli_raw_poller_callback, instance);
instance->poller_state = NfcPollerStateStarted;
}
instance->request_type = NfcCliProtocolRequestTypeNormalExecute;
furi_message_queue_put(instance->input_queue, &instance->request_type, FuriWaitForever);
furi_semaphore_acquire(instance->sem_done, FuriWaitForever);
nfc_cli_raw_print_result(instance);
}
static const NfcProtocolNameValuePair supported_protocols[] = {
{.name = "14a", .value = NfcProtocolIso14443_3a},
{.name = "iso14a", .value = NfcProtocolIso14443_3a},
{.name = "14b", .value = NfcProtocolIso14443_3b},
{.name = "iso14b", .value = NfcProtocolIso14443_3b},
{.name = "15", .value = NfcProtocolIso15693_3},
{.name = "felica", .value = NfcProtocolFelica},
};
static bool nfc_cli_raw_parse_protocol(FuriString* value, void* output) {
NfcCliRawCmdContext* ctx = output;
NfcProtocol new_protocol = NfcProtocolInvalid;
NfcCliProtocolParser* parser =
nfc_cli_protocol_parser_alloc(supported_protocols, COUNT_OF(supported_protocols));
bool result = nfc_cli_protocol_parser_get(parser, value, &new_protocol);
nfc_cli_protocol_parser_free(parser);
if(result && ctx->request.protocol != NfcProtocolInvalid &&
ctx->request.protocol != new_protocol) {
printf(
ANSI_FG_RED "Error: previous %s != new %s. Unable to continue." ANSI_RESET,
nfc_cli_get_protocol_name(ctx->request.protocol),
nfc_cli_get_protocol_name(new_protocol));
result = false;
}
if(result) {
ctx->request.protocol = new_protocol;
}
return result;
}
static bool nfc_cli_raw_parse_data(FuriString* value, void* output) {
NfcCliRawCmdContext* ctx = output;
bool result = false;
do {
size_t len = furi_string_size(value);
if(len % 2 != 0) break;
size_t data_length = len / 2;
uint8_t* data = malloc(data_length);
if(args_read_hex_bytes(value, data, data_length)) {
bit_buffer_reset(ctx->request.tx_buffer);
bit_buffer_copy_bytes(ctx->request.tx_buffer, data, data_length);
result = true;
}
free(data);
} while(false);
return result;
}
static bool nfc_cli_raw_parse_timeout(FuriString* value, void* output) {
furi_assert(value);
furi_assert(output);
NfcCliRawCmdContext* ctx = output;
bool result = false;
int timeout = 0;
if(args_read_int_and_trim(value, &timeout)) {
ctx->request.timeout = timeout;
result = true;
}
return result;
}
static bool nfc_cli_raw_parse_select(FuriString* value, void* output) {
UNUSED(value);
NfcCliRawCmdContext* ctx = output;
ctx->request.select = true;
return true;
}
static bool nfc_cli_raw_parse_crc(FuriString* value, void* output) {
UNUSED(value);
NfcCliRawCmdContext* ctx = output;
ctx->request.append_crc = true;
return true;
}
static bool nfc_cli_raw_parse_keep(FuriString* value, void* output) {
UNUSED(value);
NfcCliRawCmdContext* ctx = output;
ctx->request.keep_field = true;
return true;
}
const NfcCliKeyDescriptor raw_action_keys[] = {
{
.long_name = NULL,
.short_name = "t",
.features = {.parameter = true, .required = false},
.description = "timeout in fc",
.parse = nfc_cli_raw_parse_timeout,
},
{
.long_name = NULL,
.short_name = "k",
.description = "keep signal field ON after receive",
.parse = nfc_cli_raw_parse_keep,
},
{
.long_name = NULL,
.short_name = "c",
.description = "calculate and append CRC",
.parse = nfc_cli_raw_parse_crc,
},
{
.long_name = NULL,
.short_name = "s",
.description = "Select on FieldOn",
.parse = nfc_cli_raw_parse_select,
},
{
.long_name = "protocol",
.short_name = "p",
.description = "desired protocol. Possible values: 14a, iso14a, 14b, iso14b, 15, felica",
.features = {.parameter = true, .required = true},
.parse = nfc_cli_raw_parse_protocol,
},
{
.long_name = "data",
.short_name = "d",
.description = "Raw bytes to send in HEX format",
.features = {.parameter = true, .required = true},
.parse = nfc_cli_raw_parse_data,
},
};
const NfcCliActionDescriptor raw_action = {
.name = "raw",
.description = "Sends raw bytes using different protocols",
.key_count = COUNT_OF(raw_action_keys),
.keys = raw_action_keys,
.execute = nfc_cli_raw_execute,
.alloc = nfc_cli_raw_alloc_ctx,
.free = nfc_cli_raw_free_ctx,
.can_reuse = nfc_cli_raw_can_reuse_ctx,
};
const NfcCliActionDescriptor* raw_actions_collection[] = {&raw_action};
ADD_NFC_CLI_COMMAND(raw, "", raw_actions_collection);
//Command usage: raw <protocol> [keys] <data>
//Command examples:
//raw iso14a -sc 3000
//raw iso14a 3000
//raw iso14a 3000 -sc

View File

@@ -0,0 +1,5 @@
#pragma once
#include "../../nfc_cli_command_base_i.h"
extern const NfcCliCommandDescriptor raw_cmd;

View File

@@ -0,0 +1,101 @@
#include "nfc_cli_raw_felica.h"
#include "../../../helpers/nfc_cli_format.h"
#include <nfc/helpers/felica_crc.h>
#include <nfc/protocols/felica/felica.h>
#include <nfc/protocols/felica/felica_poller_i.h>
#define TAG "FELICA"
#define BIT_BUFFER_EMPTY(buffer) ((bit_buffer_get_size_bytes(buffer) == 0))
static inline void felica_format_activation_data(const FelicaData* data, FuriString* output) {
nfc_cli_format_array(data->idm.data, FELICA_IDM_SIZE, "IDm: ", output);
nfc_cli_format_array(data->pmm.data, FELICA_PMM_SIZE, " PMm: ", output);
}
static NfcCliRawError nfc_cli_raw_felica_process_error(FelicaError error) {
switch(error) {
case FelicaErrorNone:
return NfcCliRawErrorNone;
case FelicaErrorTimeout:
return NfcCliRawErrorTimeout;
case FelicaErrorWrongCrc:
return NfcCliRawErrorWrongCrc;
case FelicaErrorNotPresent:
return NfcCliRawErrorNotPresent;
default:
return NfcCliRawErrorProtocol;
}
}
static FelicaError nfc_cli_raw_felica_poller_process_error(NfcError error) {
switch(error) {
case NfcErrorNone:
return FelicaErrorNone;
case NfcErrorTimeout:
return FelicaErrorTimeout;
default:
return FelicaErrorNotPresent;
}
}
static inline NfcCliRawError
nfc_cli_raw_felica_activate(NfcGenericInstance* poller, FuriString* activation_string) {
FelicaData felica_data = {};
FelicaPoller* felica_poller = poller;
FURI_LOG_D(TAG, "Activating...");
FelicaError error = felica_poller_activate(felica_poller, &felica_data);
if(error == FelicaErrorNone) {
felica_format_activation_data(&felica_data, activation_string);
}
return nfc_cli_raw_felica_process_error(error);
}
static inline NfcCliRawError nfc_cli_raw_felica_txrx(
NfcGenericInstance* poller,
BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t timeout) {
FURI_LOG_D(TAG, "TxRx");
FelicaPoller* felica_poller = poller;
bit_buffer_reset(rx_buffer);
FelicaError error = FelicaErrorNone;
NfcError nfc_error = nfc_poller_trx(felica_poller->nfc, tx_buffer, rx_buffer, timeout);
if(nfc_error != NfcErrorNone) {
error = nfc_cli_raw_felica_poller_process_error(nfc_error);
} else if(!felica_crc_check(rx_buffer)) {
error = FelicaErrorWrongCrc;
}
return nfc_cli_raw_felica_process_error(error);
}
NfcCommand nfc_cli_raw_felica_handler(
NfcGenericInstance* poller,
const NfcCliRawRequest* request,
NfcCliRawResponse* const response) {
do {
if(request->select) {
response->result = nfc_cli_raw_felica_activate(poller, response->activation_string);
}
if(response->result != NfcCliRawErrorNone) break;
if(BIT_BUFFER_EMPTY(request->tx_buffer)) break;
if(request->append_crc) {
FURI_LOG_D(TAG, "Add CRC");
felica_crc_append(request->tx_buffer);
}
uint32_t timeout = request->timeout > 0 ? request->timeout : FELICA_FDT_POLL_FC;
response->result =
nfc_cli_raw_felica_txrx(poller, request->tx_buffer, response->rx_buffer, timeout);
} while(false);
return request->keep_field ? NfcCommandContinue : NfcCommandStop;
}

View File

@@ -0,0 +1,8 @@
#pragma once
#include "../nfc_cli_raw_common_types.h"
NfcCommand nfc_cli_raw_felica_handler(
NfcGenericInstance* poller,
const NfcCliRawRequest* request,
NfcCliRawResponse* const response);

View File

@@ -0,0 +1,81 @@
#include "nfc_cli_raw_iso14443_3a.h"
#include "../../../helpers/nfc_cli_format.h"
#include <nfc/helpers/iso14443_crc.h>
#include <nfc/protocols/iso14443_3a/iso14443_3a.h>
#include <nfc/protocols/iso14443_3a/iso14443_3a_poller.h>
#define TAG "ISO14A"
#define BIT_BUFFER_EMPTY(buffer) ((bit_buffer_get_size_bytes(buffer) == 0))
static NfcCliRawError nfc_cli_raw_iso14443_3a_process_error(Iso14443_3aError error) {
switch(error) {
case Iso14443_3aErrorNone:
return NfcCliRawErrorNone;
case Iso14443_3aErrorTimeout:
return NfcCliRawErrorTimeout;
case Iso14443_3aErrorWrongCrc:
return NfcCliRawErrorWrongCrc;
case Iso14443_3aErrorNotPresent:
return NfcCliRawErrorNotPresent;
default:
return NfcCliRawErrorProtocol;
}
}
static void iso14443_3a_format_activation_data(const Iso14443_3aData* data, FuriString* output) {
nfc_cli_format_array(data->uid, data->uid_len, "UID: ", output);
furi_string_cat_printf(
output, " ATQA: %02X%02X SAK: %02X", data->atqa[0], data->atqa[1], data->sak);
}
static inline NfcCliRawError
nfc_cli_raw_iso14443_3a_activate(NfcGenericInstance* poller, FuriString* activation_string) {
Iso14443_3aData iso3_data = {};
FURI_LOG_D(TAG, "Activating...");
Iso14443_3aError error = iso14443_3a_poller_activate(poller, &iso3_data);
if(error == Iso14443_3aErrorNone)
iso14443_3a_format_activation_data(&iso3_data, activation_string);
return nfc_cli_raw_iso14443_3a_process_error(error);
}
static inline NfcCliRawError nfc_cli_raw_iso14443_3a_txrx(
NfcGenericInstance* poller,
BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t timeout) {
FURI_LOG_D(TAG, "TxRx");
bit_buffer_reset(rx_buffer);
Iso14443_3aError error = iso14443_3a_poller_txrx(poller, tx_buffer, rx_buffer, timeout);
return nfc_cli_raw_iso14443_3a_process_error(error);
}
NfcCommand nfc_cli_raw_iso14443_3a_handler(
NfcGenericInstance* poller,
const NfcCliRawRequest* request,
NfcCliRawResponse* const response) {
do {
response->result = NfcCliRawErrorNone;
if(request->select) {
response->result =
nfc_cli_raw_iso14443_3a_activate(poller, response->activation_string);
}
if(response->result != NfcCliRawErrorNone) break;
if(BIT_BUFFER_EMPTY(request->tx_buffer)) break;
if(request->append_crc) {
FURI_LOG_D(TAG, "Add CRC");
iso14443_crc_append(Iso14443CrcTypeA, request->tx_buffer);
}
uint32_t timeout = request->timeout > 0 ? request->timeout : ISO14443_3A_FDT_LISTEN_FC;
response->result =
nfc_cli_raw_iso14443_3a_txrx(poller, request->tx_buffer, response->rx_buffer, timeout);
} while(false);
return request->keep_field ? NfcCommandContinue : NfcCommandStop;
}

View File

@@ -0,0 +1,8 @@
#pragma once
#include "../nfc_cli_raw_common_types.h"
NfcCommand nfc_cli_raw_iso14443_3a_handler(
NfcGenericInstance* poller,
const NfcCliRawRequest* request,
NfcCliRawResponse* const response);

View File

@@ -0,0 +1,121 @@
#include "nfc_cli_raw_iso14443_3b.h"
#include "../../../helpers/nfc_cli_format.h"
#include <nfc/helpers/iso14443_crc.h>
#include <nfc/protocols/iso14443_3b/iso14443_3b_i.h>
#include <nfc/protocols/iso14443_3b/iso14443_3b_poller_i.h>
#define TAG "ISO14B"
#define BIT_BUFFER_EMPTY(buffer) ((bit_buffer_get_size_bytes(buffer) == 0))
static NfcCliRawError nfc_cli_raw_iso14443_3b_process_error(Iso14443_3bError error) {
switch(error) {
case Iso14443_3bErrorNone:
return NfcCliRawErrorNone;
case Iso14443_3bErrorTimeout:
return NfcCliRawErrorTimeout;
case Iso14443_3bErrorWrongCrc:
return NfcCliRawErrorWrongCrc;
case Iso14443_3bErrorNotPresent:
return NfcCliRawErrorNotPresent;
default:
return NfcCliRawErrorProtocol;
}
}
static Iso14443_3bError nfc_cli_raw_iso14443_3b_poller_process_error(NfcError error) {
switch(error) {
case NfcErrorNone:
return Iso14443_3bErrorNone;
case NfcErrorTimeout:
return Iso14443_3bErrorTimeout;
default:
return Iso14443_3bErrorNotPresent;
}
}
static void iso14443_3b_format_activation_data(const Iso14443_3bData* data, FuriString* output) {
nfc_cli_format_array(data->uid, ISO14443_3B_UID_SIZE, "UID: ", output);
const Iso14443_3bProtocolInfo* info = &data->protocol_info;
furi_string_cat_printf(
output,
" BitRate: %d, Protocol: %d, Max Frame Size: %d, Fo: %d, Adc: %d, Fwi: %d",
info->bit_rate_capability,
info->protocol_type,
info->max_frame_size,
info->fo,
info->adc,
info->fwi);
}
static inline NfcCliRawError nfc_cli_raw_iso14443_3b_activate(
NfcGenericInstance* poller,
Iso14443_3bData* iso3b_data,
FuriString* activation_string) {
FURI_LOG_D(TAG, "Activating...");
Iso14443_3bError error = iso14443_3b_poller_activate(poller, iso3b_data);
if(error == Iso14443_3bErrorNone)
iso14443_3b_format_activation_data(iso3b_data, activation_string);
return nfc_cli_raw_iso14443_3b_process_error(error);
}
static inline NfcCliRawError nfc_cli_raw_iso14443_3b_txrx(
NfcGenericInstance* poller,
BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t timeout) {
FURI_LOG_D(TAG, "TxRx");
Iso14443_3bPoller* iso14b_poller = poller;
bit_buffer_reset(rx_buffer);
Iso14443_3bError error = Iso14443_3bErrorNone;
NfcError nfc_error = nfc_poller_trx(iso14b_poller->nfc, tx_buffer, rx_buffer, timeout);
if(nfc_error != NfcErrorNone) {
error = nfc_cli_raw_iso14443_3b_poller_process_error(nfc_error);
} else if(!iso14443_crc_check(Iso14443CrcTypeB, rx_buffer)) {
error = Iso14443_3bErrorWrongCrc;
}
return nfc_cli_raw_iso14443_3b_process_error(error);
}
NfcCommand nfc_cli_raw_iso14443_3b_handler(
NfcGenericInstance* poller,
const NfcCliRawRequest* request,
NfcCliRawResponse* const response) {
Iso14443_3bData iso3b_data = {0};
bool activated = false;
do {
response->result = NfcCliRawErrorNone;
if(request->select) {
response->result =
nfc_cli_raw_iso14443_3b_activate(poller, &iso3b_data, response->activation_string);
activated = response->result == NfcCliRawErrorNone;
}
if(response->result != NfcCliRawErrorNone) break;
if(BIT_BUFFER_EMPTY(request->tx_buffer)) break;
uint32_t timeout = ISO14443_3B_FDT_POLL_FC;
if(request->timeout > 0) {
timeout = request->timeout;
} else if(activated) {
timeout = iso14443_3b_get_fwt_fc_max(&iso3b_data);
}
if(request->append_crc) {
FURI_LOG_D(TAG, "Add CRC");
iso14443_crc_append(Iso14443CrcTypeB, request->tx_buffer);
}
response->result =
nfc_cli_raw_iso14443_3b_txrx(poller, request->tx_buffer, response->rx_buffer, timeout);
} while(false);
return request->keep_field ? NfcCommandContinue : NfcCommandStop;
}

View File

@@ -0,0 +1,8 @@
#pragma once
#include "../nfc_cli_raw_common_types.h"
NfcCommand nfc_cli_raw_iso14443_3b_handler(
NfcGenericInstance* poller,
const NfcCliRawRequest* request,
NfcCliRawResponse* const response);

View File

@@ -0,0 +1,102 @@
#include "nfc_cli_raw_iso15693_3.h"
#include "../../../helpers/nfc_cli_format.h"
#include <nfc/helpers/iso13239_crc.h>
#include <nfc/protocols/iso15693_3/iso15693_3.h>
#include <nfc/protocols/iso15693_3/iso15693_3_poller_i.h>
#define TAG "ISO15"
#define BIT_BUFFER_EMPTY(buffer) ((bit_buffer_get_size_bytes(buffer) == 0))
static NfcCliRawError nfc_cli_raw_iso15693_3_process_error(Iso15693_3Error error) {
switch(error) {
case Iso15693_3ErrorNone:
return NfcCliRawErrorNone;
case Iso15693_3ErrorTimeout:
return NfcCliRawErrorTimeout;
case Iso15693_3ErrorWrongCrc:
return NfcCliRawErrorWrongCrc;
case Iso15693_3ErrorNotPresent:
return NfcCliRawErrorNotPresent;
default:
return NfcCliRawErrorProtocol;
}
}
static Iso15693_3Error nfc_cli_raw_iso15693_3_poller_process_nfc_error(NfcError error) {
switch(error) {
case NfcErrorNone:
return Iso15693_3ErrorNone;
case NfcErrorTimeout:
return Iso15693_3ErrorTimeout;
default:
return Iso15693_3ErrorNotPresent;
}
}
static inline void iso15693_3_format_activation_data(const uint8_t* data, FuriString* output) {
nfc_cli_format_array(data, ISO15693_3_UID_SIZE, "UID: ", output);
}
static inline NfcCliRawError
nfc_cli_raw_iso15693_3_activate(NfcGenericInstance* poller, FuriString* activation_string) {
FURI_LOG_D(TAG, "Activating...");
Iso15693_3Poller* iso15_poller = poller;
uint8_t uid[ISO15693_3_UID_SIZE] = {0};
Iso15693_3Error error = iso15693_3_poller_inventory(iso15_poller, uid);
if(error == Iso15693_3ErrorNone) {
iso15693_3_format_activation_data(uid, activation_string);
}
return nfc_cli_raw_iso15693_3_process_error(error);
}
static inline NfcCliRawError nfc_cli_raw_iso15693_3_txrx(
NfcGenericInstance* poller,
BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t timeout) {
FURI_LOG_D(TAG, "TxRx");
Iso15693_3Poller* iso15_poller = poller;
bit_buffer_reset(rx_buffer);
Iso15693_3Error error = Iso15693_3ErrorNone;
NfcError nfc_error = nfc_poller_trx(iso15_poller->nfc, tx_buffer, rx_buffer, timeout);
if(nfc_error != NfcErrorNone) {
error = nfc_cli_raw_iso15693_3_poller_process_nfc_error(nfc_error);
} else if(!iso13239_crc_check(Iso13239CrcTypeDefault, rx_buffer)) {
error = Iso15693_3ErrorWrongCrc;
}
return nfc_cli_raw_iso15693_3_process_error(error);
}
NfcCommand nfc_cli_raw_iso15693_3_handler(
NfcGenericInstance* poller,
const NfcCliRawRequest* request,
NfcCliRawResponse* const response) {
do {
if(request->select) {
response->result =
nfc_cli_raw_iso15693_3_activate(poller, response->activation_string);
}
if(response->result != NfcCliRawErrorNone) break;
if(BIT_BUFFER_EMPTY(request->tx_buffer)) break;
if(request->append_crc) {
FURI_LOG_D(TAG, "Add CRC");
iso13239_crc_append(Iso13239CrcTypeDefault, request->tx_buffer);
}
uint32_t timeout = request->timeout > 0 ? request->timeout : ISO15693_3_FDT_POLL_FC;
response->result =
nfc_cli_raw_iso15693_3_txrx(poller, request->tx_buffer, response->rx_buffer, timeout);
} while(false);
return request->keep_field ? NfcCommandContinue : NfcCommandStop;
}

View File

@@ -0,0 +1,8 @@
#pragma once
#include "../nfc_cli_raw_common_types.h"
NfcCommand nfc_cli_raw_iso15693_3_handler(
NfcGenericInstance* poller,
const NfcCliRawRequest* request,
NfcCliRawResponse* const response);

View File

@@ -0,0 +1,28 @@
#pragma once
#include <furi.h>
#include <nfc/nfc.h>
#include <nfc/nfc_poller.h>
typedef enum {
NfcCliRawErrorNone,
NfcCliRawErrorTimeout,
NfcCliRawErrorNotPresent,
NfcCliRawErrorWrongCrc,
NfcCliRawErrorProtocol,
} NfcCliRawError;
typedef struct {
bool select;
bool keep_field;
bool append_crc;
NfcProtocol protocol;
BitBuffer* tx_buffer;
uint32_t timeout;
} NfcCliRawRequest;
typedef struct {
NfcCliRawError result;
BitBuffer* rx_buffer;
FuriString* activation_string;
} NfcCliRawResponse;

View File

@@ -0,0 +1,124 @@
#include "nfc_cli_commands.h"
#include "nfc_cli_command_processor.h"
#include "applications/services/loader/loader.h"
#include "applications/services/cli/cli_main_commands.h"
#include <toolbox/cli/shell/cli_shell.h>
#include <toolbox/cli/cli_registry.h>
#define NFC_DESKTOP_APP_NAME "NFC"
#define TAG "NfcCli"
#define NFC_PROMPT "[" ANSI_FG_GREEN "nfc" ANSI_RESET "]"
typedef struct {
Nfc* nfc;
CliRegistry* registry;
CliShell* shell;
NfcCliProcessorContext* processor_context;
} NfcCliContext;
static void nfc_cli_shell_motd(void* context) {
UNUSED(context);
printf(ANSI_FG_BR_BLUE "\r\n"
" 0000 \r\n"
" 0000 \r\n"
" 000 0000 \r\n"
" 0000 00000 \r\n"
" 000 00000 0000 \r\n"
" 0 0000 0000 00000 \r\n"
" 000000 0000 00000 0000 \r\n"
" 00000000 0000 0000 0000 \r\n"
" 0000000000 0000 00000 0000 \r\n"
" 0000 00000000 00000 00000 0000 \r\n"
" 0000 0000000 00000 00000 0000 \r\n"
" 0000 000000000000 0000 0000 \r\n"
" 00000 000000000 00000 0000 \r\n"
" 00 000000 0000 00000 \r\n"
" 00 00000 0000 \r\n"
" 0000 00000 \r\n"
" 000 0000 \r\n"
" 0000 \r\n"
" 0005 \r\n"
"\r\n" ANSI_FG_BR_WHITE "Welcome to NFC Command Line Interface!\r\n"
"Run `help` or `?` to list available commands\r\n" ANSI_RESET);
}
static void nfc_cli_subscribe_commands(NfcCliContext* instance) {
size_t cnt = nfc_cli_command_get_count();
for(size_t i = 0; i < cnt; i++) {
const NfcCliCommandDescriptor* cmd = nfc_cli_command_get_by_index(i);
CliCommandExecuteCallback callback = nfc_cli_command_get_execute(cmd);
if(callback == NULL) continue;
const char* name = nfc_cli_command_get_name(cmd);
cli_registry_add_command(
instance->registry,
name,
CliCommandFlagParallelSafe,
callback,
instance->processor_context);
}
}
static bool nfc_cli_desktop_app_is_running() {
FuriString* app_name = furi_string_alloc();
Loader* ldr = furi_record_open(RECORD_LOADER);
bool result = false;
if(loader_get_application_name(ldr, app_name)) {
result = furi_string_equal_str(app_name, NFC_DESKTOP_APP_NAME);
}
furi_record_close(RECORD_LOADER);
furi_string_free(app_name);
return result;
}
static NfcCliContext* nfc_cli_alloc(PipeSide* pipe) {
NfcCliContext* instance = malloc(sizeof(NfcCliContext));
instance->nfc = nfc_alloc();
instance->processor_context = nfc_cli_command_processor_alloc(instance->nfc);
instance->registry = cli_registry_alloc();
nfc_cli_subscribe_commands(instance);
instance->shell =
cli_shell_alloc(nfc_cli_shell_motd, instance, pipe, instance->registry, NULL);
cli_shell_set_prompt(instance->shell, NFC_PROMPT);
return instance;
}
void nfc_cli_free(NfcCliContext* instance) {
furi_assert(instance);
nfc_cli_command_processor_free(instance->processor_context);
cli_shell_free(instance->shell);
cli_registry_free(instance->registry);
nfc_free(instance->nfc);
free(instance);
}
void nfc_cli_execute(PipeSide* pipe, FuriString* args, void* context) {
furi_assert(pipe);
UNUSED(args);
UNUSED(context);
if(nfc_cli_desktop_app_is_running()) {
printf(ANSI_FG_YELLOW
"NFC app is running, unable to run NFC CLI at the same time!\r\n" ANSI_RESET);
return;
}
NfcCliContext* instance = nfc_cli_alloc(pipe);
cli_shell_start(instance->shell);
cli_shell_join(instance->shell);
nfc_cli_free(instance);
}
CLI_COMMAND_INTERFACE(nfc, nfc_cli_execute, CliCommandFlagParallelSafe, 1024, CLI_APPID);

View File

@@ -0,0 +1,69 @@
#pragma once
#include <furi.h>
#include <toolbox/cli/cli_command.h>
#include <nfc/nfc.h>
/**
* @brief Type for action context to be created before action execution
* must be hanlded through callbacks in each action separately
*/
typedef void NfcCliActionContext;
/**
* @brief Callback type for function of action context allocation
* @param nfc Instance of NFC subsystem, will be used during action execution
* @return Pointer to action context
*/
typedef NfcCliActionContext* (*NfcCliActionContextAlloc)(Nfc* nfc);
/**
* @brief Callback for action context deleting
* @param action_ctx Action context to be freed
*/
typedef void (*NfcCliActionContextFree)(NfcCliActionContext* action_ctx);
/**
* @brief Callback invoked by command processor to determine whether already
* existing context (from previously executed command) can be reused for the new one.
*
* @param action_ctx Action context
*
* In most cases re-creating of a new context is not needed.
* It is used in 'raw' command, where nfc field sometimes need to stay turned on between
* commands.
*
* Handling of this situation and decision about reusing action context is on developer,
* who need to decide, can this command reuse context.
*
* It can be done by comparing parameters of previously executed command and a new one,
* or by some args in command.
*
* See implementation of 'keep_field' flag in 'raw' command for example.
*/
typedef bool (*NfcCliActionContextCanReuse)(NfcCliActionContext* ctx);
/**
* @brief Action execution callback
* @param pipe provided by cli shell, can be used for command termination
* @param ctx Action context
*/
typedef void (*NfcCliActionHandlerCallback)(PipeSide* pipe, NfcCliActionContext* ctx);
/**
* @brief Callback used for parsing argument key.
* Each key added to command must have this, otherwise parsing result will always be
* false and command will never be executed.
*
* @param value Text value for the key to be parsed
* @param ctx Action context
* @return true when parsing was fine, otherwise false. If any argument in command input
* generates false during parsing, command will not be executed and error will be shown
*/
typedef bool (*NfcCliArgParseCallback)(FuriString* value, NfcCliActionContext* ctx);
typedef struct NfcCliKeyDescriptor NfcCliKeyDescriptor;
typedef struct NfcCliActionDescriptor NfcCliActionDescriptor;
typedef struct NfcCliCommandDescriptor NfcCliCommandDescriptor;

View File

@@ -0,0 +1,130 @@
#pragma once
#include "nfc_cli_command_base.h"
#include <toolbox/cli/cli_ansi.h>
#include <nfc/nfc.h>
#include <nfc/protocols/nfc_protocol.h>
#include "nfc_cli_command_processor.h"
/**
* @brief How to add command.
*
* There are 3 possible option on how to add new command to nfc_cli:
*
* @see Option 1 "Add action command directly to nfc_shell"
*
* In this case command will be invoked with argument string from nfc_shell.
* Command registration must be performed directly by user.
*
* Steps:
* 1. Add new function for command to nfc_cli.c
* 2. In nfc_cli_alloc function register command using cli_registry_add_command after nfc_cli_subscribe_commands
*
* This option is NOT RECOMENDED, because such command will not have any 'help'
* processing and parsing error checks. Argument parsing must also be done by hand.
*
* --------------------------------------------------------------------------
*
* @see Option 2 "Add action command to collection without further processing"
*
* In this case command will be invoked with argument string from nfc_shell.
* nfc_cli_command_processor is skipped, so argument handling is up to the developer.
*
* Steps:
* 1. Add new pair of nfc_cli_command_<cmd>.c/.h files to /commands folder
* 2. Define const NfcCliCommandDescriptor instance in .c file and its extern definition in .h file
* 3. Include .h file to nfc_cli_commands.c file below comment "Include new commands here"
* 4. Add new command reference to nfc_cli_commands array
* 5. Add path to nfc_cli_command_<cmd>.c file into 'cli_nfc' plugin in application.fam file
*
* This option suites for simple commands with no any parameters.
* @see nfc_cli_command_field.c implementation as an example.
*
* --------------------------------------------------------------------------
*
* @see Option 3 "Add action command to collection with full processing"
*
* In this nfc_cli_command_processor will be invoked for parsing command arguments
* and action execution. Also it will handle errors and help printing.
*
* Steps:
* 1. Add new pair of nfc_cli_command_<cmd>.c/.h files to /commands folder
* 2. Use macro ADD_NFC_CLI_COMMAND to define command in .c file
* 3. Define command extern in .h file, using command name from macro in form of "<name>_cmd"
* 4. Add all desired actions and keys to your command
* 5. Include .h file to nfc_cli_commands.c file below comment "Include new commands here"
* 6. Add new command reference to nfc_cli_commands array
* 7. Add path to nfc_cli_command_<cmd>.c file into 'cli_nfc' plugin in application.fam file
*
* This option suites for "difficult" commands which has actions with lots of keys.
* @see nfc_cli_command_emulate.c implementation as an example.
*
*/
/**
* @brief Used to decorate argument with some properties
*/
typedef struct {
bool required : 1; /**< Command always needs this argument. Missing arguments with this set to true will result execution error.*/
bool parameter : 1; /**< Such argument requires value after its name, otherwise it is a simple on/off switch */
bool multivalue : 1; /**< Such argument can take multiple values after its name, like this "-key value1 value2 .. valueN" */
} FURI_PACKED NfcCliKeyFeatureSupport;
/**
* @brief Describes key for action
*/
struct NfcCliKeyDescriptor {
NfcCliKeyFeatureSupport features; /**< Features supported defining key behaviour */
const char* long_name; /**< Long key name starts with '--' symbol in argument string */
const char* short_name; /**< Short key name starts with '-' symbol in argument string */
const char* description; /**< Key description showed in help */
NfcCliArgParseCallback parse; /**< Parsing callback */
};
/**
* @brief Describes action
*/
struct NfcCliActionDescriptor {
const char* name; /**< Action name MUST be the first argument after command.*/
const char* description; /**< Description showed in help */
size_t key_count; /**< Amount of key entries in keys array */
const NfcCliKeyDescriptor* keys; /**< Keys available for action */
NfcCliActionHandlerCallback execute; /**< Action callback, invoked if parsing is ok */
NfcCliActionContextAlloc alloc; /**< Allocates action context during command processing */
NfcCliActionContextFree free; /**< Frees action context */
NfcCliActionContextCanReuse can_reuse; /**< Checks context reuse possibility */
};
/**
* @brief Describes command
*/
struct NfcCliCommandDescriptor {
const char* name; /** Used to register command in cli shell */
const char* description; /**< Description showed in help */
size_t action_count; /** Amount of actions available in scope of this particular command */
const NfcCliActionDescriptor** actions; /**< Actions available for command */
CliCommandExecuteCallback callback; /** Entry point for command */
};
/**
* @brief This macro simplifies command creation. It fills instance of
* NfcCliCommandDescriptor and generates a callback which invokes
* nfc_cli_command_processor inside
*/
#define ADD_NFC_CLI_COMMAND(name, description, actions) \
static void nfc_cli_command_##name##_callback( \
PipeSide* pipe, FuriString* args, void* context); \
\
const NfcCliCommandDescriptor name##_cmd = { \
#name, \
#description, \
COUNT_OF(actions), \
actions, \
nfc_cli_command_##name##_callback, \
}; \
\
static void nfc_cli_command_##name##_callback( \
PipeSide* pipe, FuriString* args, void* context) { \
nfc_cli_command_processor_run(&name##_cmd, pipe, args, context); \
}

View File

@@ -0,0 +1,388 @@
#include "nfc_cli_command_processor.h"
#include "nfc_cli_commands.h"
#include "nfc_cli_command_base_i.h"
#include <m-string.h>
#include <args.h>
#include <hex.h>
#define TAG "NfcCliProcessor"
#define NFC_CLI_KEYS_FOUND_SIZE_BYTES (10 * sizeof(NfcCliKeyDescriptor*))
typedef enum {
NfcCliArgumentTypeShortNameKey,
NfcCliArgumentTypeShortNameKeyGroup,
NfcCliArgumentTypeLongNameKey,
NfcCliArgumentTypeUnknown
} NfcCliArgumentType;
/**
* @brief Error codes for different processing states
*/
typedef enum {
NfcCliProcessorErrorNone, /**< Command was parsed successfully and execute callback will be invoked*/
NfcCliProcessorErrorNoneButHelp, /**< There was no error, but help needs to be printed. Command wil not be executed */
NfcCliProcessorErrorActionNotFound, /**< Wrong action was passed as first command parameter */
NfcCliProcessorErrorKeyNotSupported, /**< Unsupported key was passed in arguments. Details will be printed in erro_message*/
NfcCliProcessorErrorKeyParameterInGroup, /**< Parameter which requires value was passed in group. Example: -sckd */
NfcCliProcessorErrorKeyParameterValueMissing, /**< Value is missing for the parameter which requires it */
NfcCliProcessorErrorKeyDuplication, /**< Some argument key was duplicated in input parameters */
NfcCliProcessorErrorKeyParseError, /**< Error happened during argument value parsing */
NfcCliProcessorErrorKeyRequiredMissing, /**< Some keys required for command execution is missing*/
NfcCliProcessorErrorNum
} NfcCliProcessorError;
struct NfcCliProcessorContext {
const NfcCliCommandDescriptor* cmd;
const NfcCliActionDescriptor* action;
const NfcCliKeyDescriptor** keys_found;
uint8_t total_keys_found;
uint8_t required_keys_expected;
uint8_t required_keys_found;
Nfc* nfc;
void* action_context;
FuriString* error_message;
};
static const NfcCliActionDescriptor*
nfc_cli_get_action_from_args(const NfcCliCommandDescriptor* cmd, FuriString* args) {
const NfcCliActionDescriptor* action = cmd->actions[0];
bool multiple_action_cmd = nfc_cli_command_has_multiple_actions(cmd);
if(multiple_action_cmd) {
action = NULL;
FuriString* arg_str = furi_string_alloc();
if(args_read_string_and_trim(args, arg_str)) {
action = nfc_cli_command_get_action_by_name(cmd, arg_str);
}
furi_string_free(arg_str);
}
return action;
}
static bool nfc_cli_action_can_reuse_context(
NfcCliProcessorContext* instance,
const NfcCliActionDescriptor* new_action) {
bool result = false;
do {
if(instance->action != new_action) break;
if(new_action->can_reuse == NULL) break;
result = new_action->can_reuse(instance->action_context);
} while(false);
return result;
}
static void nfc_cli_action_free(NfcCliProcessorContext* instance) {
if(instance->action && instance->action->free) {
FURI_LOG_D(TAG, "Free previous \"%s\" action context", instance->action->name);
instance->action->free(instance->action_context);
}
instance->action = NULL;
}
static NfcCliProcessorError
nfc_cli_action_alloc(NfcCliProcessorContext* instance, FuriString* args) {
const NfcCliCommandDescriptor* cmd = instance->cmd;
NfcCliProcessorError result = NfcCliProcessorErrorNone;
do {
const NfcCliActionDescriptor* action = nfc_cli_get_action_from_args(cmd, args);
if(action == NULL) {
result = NfcCliProcessorErrorActionNotFound;
furi_string_printf(instance->error_message, "Action not found");
break;
}
if(!nfc_cli_action_can_reuse_context(instance, action)) {
nfc_cli_action_free(instance);
instance->action = action;
if(action->alloc && action->free) {
FURI_LOG_D(TAG, "Allocating context for action \"%s\"", action->name);
instance->action_context = instance->action->alloc(instance->nfc);
} else if(action->alloc && (action->free == NULL)) {
FURI_LOG_W(
TAG,
"Free callback not defined for action \"%s\". Skip allocation to avoid memory leak.",
action->name);
instance->action_context = NULL;
} else {
FURI_LOG_D(TAG, "No alloc context callback for action \"%s\"", action->name);
instance->action_context = NULL;
}
} else
FURI_LOG_D(TAG, "Reusing context from previous \"%s\" action", action->name);
memset(instance->keys_found, 0, NFC_CLI_KEYS_FOUND_SIZE_BYTES);
instance->required_keys_expected = nfc_cli_action_get_required_keys_count(action);
instance->required_keys_found = 0;
instance->total_keys_found = 0;
} while(false);
return result;
}
static NfcCliArgumentType nfc_cli_get_argument_type(FuriString* argument) {
size_t arg_len = furi_string_size(argument);
NfcCliArgumentType type = NfcCliArgumentTypeUnknown;
if(arg_len > 2) {
char ch1 = furi_string_get_char(argument, 0);
char ch2 = furi_string_get_char(argument, 1);
if(ch1 == '-') {
type = (ch2 == '-') ? NfcCliArgumentTypeLongNameKey :
NfcCliArgumentTypeShortNameKeyGroup;
}
} else if(arg_len == 2) {
char ch1 = furi_string_get_char(argument, 0);
type = (ch1 == '-') ? NfcCliArgumentTypeShortNameKey : NfcCliArgumentTypeUnknown;
}
return type;
}
static bool
nfc_cli_check_duplicate_keys(NfcCliProcessorContext* instance, const NfcCliKeyDescriptor* key) {
bool result = false;
for(size_t i = 0; i < instance->total_keys_found; i++) {
const NfcCliKeyDescriptor* buf = instance->keys_found[i];
if(buf != key) continue;
result = true;
break;
}
return result;
}
static void nfc_cli_trim_multivalue_arg(FuriString* args, FuriString* value) {
furi_string_set(value, args);
size_t index = furi_string_search_char(value, '-', 0);
if(index != STRING_FAILURE) {
furi_string_left(value, index);
furi_string_right(args, index);
} else {
furi_string_reset(args);
}
}
static NfcCliProcessorError nfc_cli_parse_single_key(
NfcCliProcessorContext* instance,
FuriString* argument,
FuriString* args,
bool from_group) {
FuriString* value_str = furi_string_alloc();
NfcCliProcessorError result = NfcCliProcessorErrorNone;
do {
const NfcCliKeyDescriptor* key =
nfc_cli_action_get_key_descriptor(instance->action, argument);
if(key == NULL) {
if(furi_string_equal_str(argument, "h"))
result = NfcCliProcessorErrorNoneButHelp;
else {
furi_string_printf(
instance->error_message,
"Key \'%s\' is not supported",
furi_string_get_cstr(argument));
result = NfcCliProcessorErrorKeyNotSupported;
}
break;
}
if(key->features.parameter && from_group) {
furi_string_printf(
instance->error_message,
"Parameter key \'%s\' can\'t be grouped",
furi_string_get_cstr(argument));
result = NfcCliProcessorErrorKeyParameterInGroup;
break;
}
if(nfc_cli_check_duplicate_keys(instance, key)) {
furi_string_printf(
instance->error_message, "Duplicated key \'%s\'", furi_string_get_cstr(argument));
result = NfcCliProcessorErrorKeyDuplication;
break;
}
if(key->features.multivalue && !key->features.parameter) break;
if(key->features.multivalue) {
nfc_cli_trim_multivalue_arg(args, value_str);
FURI_LOG_D(TAG, "Multivalue: %s", furi_string_get_cstr(value_str));
} else if(key->features.parameter && !args_read_string_and_trim(args, value_str)) {
result = NfcCliProcessorErrorKeyParameterValueMissing;
furi_string_printf(
instance->error_message,
"Missing value for \'%s\'",
furi_string_get_cstr(argument));
break;
}
if(key->parse == NULL) {
furi_string_printf(
instance->error_message,
"Parse callback for key \'%s\' not defined",
furi_string_get_cstr(argument));
result = NfcCliProcessorErrorKeyParseError;
break;
}
FURI_LOG_D(TAG, "Parsing key \"%s\"", furi_string_get_cstr(argument));
if(!key->parse(value_str, instance->action_context)) {
furi_string_printf(
instance->error_message,
"Unable to parse value \'%s\' for key \'%s\'",
furi_string_get_cstr(value_str),
furi_string_get_cstr(argument));
result = NfcCliProcessorErrorKeyParseError;
break;
}
instance->keys_found[instance->total_keys_found] = key;
instance->total_keys_found++;
if(key->features.required) instance->required_keys_found++;
} while(false);
furi_string_free(value_str);
return result;
}
static NfcCliProcessorError
nfc_cli_parse_group_key(NfcCliProcessorContext* instance, FuriString* argument) {
NfcCliProcessorError result = NfcCliProcessorErrorNone;
FURI_LOG_D(TAG, "Parsing key group\"%s\"", furi_string_get_cstr(argument));
FuriString* arg_buf = furi_string_alloc();
for(size_t i = 0; i < furi_string_size(argument); i++) {
furi_string_set_n(arg_buf, argument, i, 1);
result = nfc_cli_parse_single_key(instance, arg_buf, NULL, true);
if(result != NfcCliProcessorErrorNone) break;
}
furi_string_free(arg_buf);
return result;
}
static NfcCliProcessorError nfc_cli_parse_argument(
NfcCliProcessorContext* instance,
FuriString* argument,
FuriString* args) {
NfcCliArgumentType type = nfc_cli_get_argument_type(argument);
furi_string_trim(argument, "-");
NfcCliProcessorError result = NfcCliProcessorErrorNone;
if(type == NfcCliArgumentTypeShortNameKeyGroup)
result = nfc_cli_parse_group_key(instance, argument);
else if((type == NfcCliArgumentTypeShortNameKey) || (type == NfcCliArgumentTypeLongNameKey)) {
result = nfc_cli_parse_single_key(instance, argument, args, false);
} else if(type == NfcCliArgumentTypeUnknown) { //-V547
result = NfcCliProcessorErrorKeyNotSupported;
furi_string_printf(
instance->error_message,
"Key \'%s\' is not supported",
furi_string_get_cstr(argument));
}
return result;
}
static NfcCliProcessorError
nfc_cli_process_arguments(NfcCliProcessorContext* instance, FuriString* args) {
NfcCliProcessorError result = NfcCliProcessorErrorNone;
FuriString* argument = furi_string_alloc();
while(args_read_string_and_trim(args, argument)) {
result = nfc_cli_parse_argument(instance, argument, args);
if(result != NfcCliProcessorErrorNone) break;
}
furi_string_free(argument);
if((result == NfcCliProcessorErrorNone) &&
(instance->required_keys_expected != instance->required_keys_found)) {
furi_string_printf(instance->error_message, "Some required keys missing");
result = NfcCliProcessorErrorKeyRequiredMissing;
}
return result;
}
static inline void nfc_cli_command_process_error(
const NfcCliProcessorContext* instance,
NfcCliProcessorError error) {
do {
if(error == NfcCliProcessorErrorNone) break;
if(error != NfcCliProcessorErrorNoneButHelp)
printf(
ANSI_FG_RED "Error: %s\r\n" ANSI_RESET,
furi_string_get_cstr(instance->error_message));
if(error == NfcCliProcessorErrorActionNotFound)
nfc_cli_command_format_info(instance->cmd, instance->error_message);
else
nfc_cli_action_format_info(instance->action, instance->error_message);
printf("\n%s", furi_string_get_cstr(instance->error_message));
} while(false);
}
void nfc_cli_command_processor_run(
const NfcCliCommandDescriptor* cmd,
PipeSide* pipe,
FuriString* args,
void* context) {
furi_assert(pipe);
furi_assert(cmd);
furi_assert(args);
NfcCliProcessorContext* instance = context;
furi_string_reset(instance->error_message);
NfcCliProcessorError error = NfcCliProcessorErrorNone;
instance->cmd = cmd;
do {
error = nfc_cli_action_alloc(instance, args);
if(error != NfcCliProcessorErrorNone) break;
error = nfc_cli_process_arguments(instance, args);
if(error != NfcCliProcessorErrorNone) break;
if(instance->action && instance->action->execute) {
instance->action->execute(pipe, instance->action_context);
} else {
FURI_LOG_W(TAG, "Action execute callback missing");
}
} while(false);
nfc_cli_command_process_error(instance, error);
}
NfcCliProcessorContext* nfc_cli_command_processor_alloc(Nfc* nfc) {
furi_assert(nfc);
NfcCliProcessorContext* instance = malloc(sizeof(NfcCliProcessorContext));
instance->nfc = nfc;
instance->keys_found = malloc(NFC_CLI_KEYS_FOUND_SIZE_BYTES);
instance->total_keys_found = 0;
instance->required_keys_found = 0;
instance->required_keys_expected = 0;
instance->error_message = furi_string_alloc();
return instance;
}
void nfc_cli_command_processor_free(NfcCliProcessorContext* instance) {
furi_assert(instance);
nfc_cli_action_free(instance);
free(instance->keys_found);
furi_string_free(instance->error_message);
instance->nfc = NULL;
free(instance);
}

View File

@@ -0,0 +1,15 @@
#pragma once
#include <furi.h>
#include <nfc/nfc.h>
#include "nfc_cli_command_base.h"
typedef struct NfcCliProcessorContext NfcCliProcessorContext;
NfcCliProcessorContext* nfc_cli_command_processor_alloc(Nfc* nfc);
void nfc_cli_command_processor_free(NfcCliProcessorContext* instance);
void nfc_cli_command_processor_run(
const NfcCliCommandDescriptor* cmd,
PipeSide* pipe,
FuriString* args,
void* context);

View File

@@ -0,0 +1,161 @@
#include "nfc_cli_commands.h"
#include "nfc_cli_command_base_i.h"
/** Include new commands here */
#include "commands/raw/nfc_cli_command_raw.h"
#include "commands/apdu/nfc_cli_command_apdu.h"
#include "commands/dump/nfc_cli_command_dump.h"
#include "commands/mfu/nfc_cli_command_mfu.h"
#include "commands/nfc_cli_command_emulate.h"
#include "commands/nfc_cli_command_scanner.h"
#include "commands/nfc_cli_command_field.h"
#define TAG "NfcCliCommands"
/** Add new commands here */
static const NfcCliCommandDescriptor* nfc_cli_commands[] = {
&apdu_cmd,
&raw_cmd,
&emulate_cmd,
&mfu_cmd,
&scanner_cmd,
&dump_cmd,
&field_cmd,
};
size_t nfc_cli_command_get_count() {
return COUNT_OF(nfc_cli_commands);
}
const NfcCliActionDescriptor*
nfc_cli_command_get_action_by_name(const NfcCliCommandDescriptor* cmd, const FuriString* name) {
furi_assert(cmd);
furi_assert(name);
for(size_t i = 0; i < cmd->action_count; i++) {
const NfcCliActionDescriptor* action = cmd->actions[i];
if(furi_string_equal_str(name, action->name)) return action;
}
return NULL;
}
const NfcCliCommandDescriptor* nfc_cli_command_get_by_index(size_t index) {
furi_assert(index < COUNT_OF(nfc_cli_commands));
return nfc_cli_commands[index];
}
bool nfc_cli_command_has_multiple_actions(const NfcCliCommandDescriptor* cmd) {
furi_assert(cmd);
furi_check(cmd->action_count > 0);
return (cmd->action_count > 1);
}
const char* nfc_cli_command_get_name(const NfcCliCommandDescriptor* cmd) {
furi_assert(cmd);
return cmd->name;
}
CliCommandExecuteCallback nfc_cli_command_get_execute(const NfcCliCommandDescriptor* cmd) {
furi_assert(cmd);
return cmd->callback;
}
static inline const NfcCliKeyDescriptor* nfc_cli_action_get_key_by_name(
const NfcCliActionDescriptor* action,
const FuriString* name,
bool long_name) {
for(size_t i = 0; i < action->key_count; i++) {
const NfcCliKeyDescriptor* key = &action->keys[i];
const char* buf = long_name ? key->long_name : key->short_name;
if((buf != NULL) && furi_string_equal_str(name, buf)) return key;
}
return NULL;
}
const NfcCliKeyDescriptor*
nfc_cli_action_get_key_descriptor(const NfcCliActionDescriptor* action, FuriString* argument) {
furi_assert(action);
furi_assert(argument);
return nfc_cli_action_get_key_by_name(action, argument, furi_string_size(argument) > 1);
}
size_t nfc_cli_action_get_required_keys_count(const NfcCliActionDescriptor* action) {
furi_assert(action);
size_t required_key_count = 0;
for(size_t i = 0; i < action->key_count; i++) {
const NfcCliKeyDescriptor* key = &action->keys[i];
if(!key->features.required) continue;
required_key_count++;
}
return required_key_count;
}
static int nfc_cli_action_format_key_name(const NfcCliKeyDescriptor* key, FuriString* output) {
int len = 0;
FuriString* name = furi_string_alloc();
if(key->short_name && key->long_name) {
len = furi_string_printf(name, "-%s, --%s", key->short_name, key->long_name);
} else if(key->short_name && (key->long_name == NULL)) {
len = furi_string_printf(name, "-%s", key->short_name);
} else if((key->short_name == NULL) && key->long_name) {
len = furi_string_printf(name, "--%s", key->long_name);
}
const char* color = key->features.required ? ANSI_FLIPPER_BRAND_ORANGE : ANSI_RESET;
furi_string_printf(output, "%s%s%s", color, furi_string_get_cstr(name), ANSI_RESET);
furi_string_free(name);
return len;
}
void nfc_cli_action_format_info(const NfcCliActionDescriptor* action, FuriString* output) {
furi_assert(action);
furi_assert(output);
furi_string_printf(
output,
action->description ? "%s - %s\r\n\n" : "%s\r\n\n",
action->name,
action->description);
if(action->key_count == 0) return;
FuriString* buf = furi_string_alloc();
furi_string_cat_printf(
output,
ANSI_FG_BR_GREEN "Keys " ANSI_FLIPPER_BRAND_ORANGE "(required) " ANSI_RESET
"(optional):\r\n");
for(size_t i = 0; i < action->key_count; i++) {
const NfcCliKeyDescriptor* key = &action->keys[i];
int len = nfc_cli_action_format_key_name(key, buf);
furi_string_cat_printf(output, "%s", furi_string_get_cstr(buf));
if(key->description) {
const int offset = 20;
furi_string_cat_printf(
output, ANSI_CURSOR_RIGHT_BY("%d") "%s", offset - len, key->description);
}
furi_string_cat_printf(output, "\r\n");
}
furi_string_free(buf);
}
void nfc_cli_command_format_info(const NfcCliCommandDescriptor* cmd, FuriString* output) {
furi_assert(cmd);
furi_assert(output);
furi_string_printf(output, "%s - %s\r\n", cmd->name, cmd->description);
if(cmd->action_count > 1) {
furi_string_cat_printf(output, "Possible actions: \r\n");
for(size_t i = 0; i < cmd->action_count; i++) {
const NfcCliActionDescriptor* action = cmd->actions[i];
furi_string_cat_printf(
output,
action->description ? "\t%s\t-\t%s\r\n" : "%s\r\n",
action->name,
action->description);
}
}
}

View File

@@ -0,0 +1,27 @@
#pragma once
#include "nfc_cli_command_base.h"
#include <toolbox/cli/cli_command.h>
#include <toolbox/cli/cli_ansi.h>
size_t nfc_cli_command_get_count();
const NfcCliCommandDescriptor* nfc_cli_command_get_by_index(size_t index);
const char* nfc_cli_command_get_name(const NfcCliCommandDescriptor* cmd);
CliCommandExecuteCallback nfc_cli_command_get_execute(const NfcCliCommandDescriptor* cmd);
bool nfc_cli_command_has_multiple_actions(const NfcCliCommandDescriptor* cmd);
const NfcCliActionDescriptor*
nfc_cli_command_get_action_by_name(const NfcCliCommandDescriptor* cmd, const FuriString* name);
size_t nfc_cli_action_get_required_keys_count(const NfcCliActionDescriptor* action);
const NfcCliKeyDescriptor*
nfc_cli_action_get_key_descriptor(const NfcCliActionDescriptor* action, FuriString* argument);
void nfc_cli_command_format_info(const NfcCliCommandDescriptor* cmd, FuriString* output);
void nfc_cli_action_format_info(const NfcCliActionDescriptor* action, FuriString* output);

View File

@@ -21,7 +21,7 @@ typedef struct {
uint32_t ar1;
} Mfkey32LoggerParams;
ARRAY_DEF(Mfkey32LoggerParams, Mfkey32LoggerParams, M_POD_OPLIST);
ARRAY_DEF(Mfkey32LoggerParams, Mfkey32LoggerParams, M_POD_OPLIST); //-V658
struct Mfkey32Logger {
uint32_t cuid;

View File

@@ -27,7 +27,7 @@ typedef struct {
NfcSupportedCardsPluginFeature feature;
} NfcSupportedCardsPluginCache;
ARRAY_DEF(NfcSupportedCardsPluginCache, NfcSupportedCardsPluginCache, M_POD_OPLIST);
ARRAY_DEF(NfcSupportedCardsPluginCache, NfcSupportedCardsPluginCache, M_POD_OPLIST); //-V658
typedef enum {
NfcSupportedCardsLoadStateIdle,

View File

@@ -39,17 +39,8 @@ static bool nfc_scene_info_on_event_felica(NfcApp* instance, SceneManagerEvent e
}
static void nfc_scene_more_info_on_enter_felica(NfcApp* instance) {
const NfcDevice* device = instance->nfc_device;
const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica);
FuriString* temp_str = furi_string_alloc();
nfc_render_felica_dump(data, temp_str);
widget_add_text_scroll_element(
instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));
furi_string_free(temp_str);
// Jump to advanced scene right away
scene_manager_next_scene(instance->scene_manager, NfcSceneFelicaMoreInfo);
}
static NfcCommand nfc_scene_read_poller_callback_felica(NfcGenericEvent event, void* context) {

View File

@@ -4,9 +4,16 @@ void nfc_render_felica_blocks_count(
const FelicaData* data,
FuriString* str,
bool render_auth_notification) {
furi_string_cat_printf(str, "\nBlocks Read: %u/%u", data->blocks_read, data->blocks_total);
if(render_auth_notification && data->blocks_read != data->blocks_total) {
furi_string_cat_printf(str, "\nAuth-protected blocks!");
if(data->workflow_type == FelicaLite) {
furi_string_cat_printf(str, "Blocks: %u\n", data->blocks_total);
furi_string_cat_printf(str, "\nBlocks Read: %u/%u", data->blocks_read, data->blocks_total);
if(render_auth_notification && data->blocks_read != data->blocks_total) {
furi_string_cat_printf(str, "\nAuth-protected blocks!");
}
} else if(data->workflow_type == FelicaStandard) {
furi_string_cat_printf(
str, "Public blocks Read: %lu", simple_array_get_count(data->public_blocks));
}
}
@@ -32,6 +39,11 @@ void nfc_render_felica_info(
furi_string_cat_printf(str, "Tech: JIS X 6319-4,\nISO 18092 [NFC-F]\n");
}
FuriString* ic_type_str = furi_string_alloc();
felica_get_ic_name(data, ic_type_str);
furi_string_cat_printf(str, "IC Type:\n%s\n", furi_string_get_cstr(ic_type_str));
furi_string_free(ic_type_str);
nfc_render_felica_idm(data, format_type, str);
if(format_type == NfcProtocolFormatTypeFull) {
@@ -40,6 +52,14 @@ void nfc_render_felica_info(
furi_string_cat_printf(str, "%02X ", data->pmm.data[i]);
}
}
furi_string_cat_printf(str, "\n");
furi_string_cat_printf(
str,
"Services found: %lu \nAreas found: %lu\n",
simple_array_get_count(data->services),
simple_array_get_count(data->areas));
nfc_render_felica_blocks_count(data, str, true);
}
@@ -59,13 +79,18 @@ static void nfc_render_felica_block_name(
static void nfc_render_felica_block_data(const FelicaBlock* block, FuriString* str) {
furi_string_cat_printf(str, "\nSF1=%02X; SF2=%02X\n", block->SF1, block->SF2);
for(size_t j = 0; j < FELICA_DATA_BLOCK_SIZE; j++) {
if((j != 0) && (j % 8 == 0)) furi_string_cat_printf(str, "\n");
furi_string_cat_printf(str, "%02X ", block->data[j]);
for(size_t i = 0; i < FELICA_DATA_BLOCK_SIZE; i += 2) {
furi_string_cat_printf(str, "%02X%02X ", block->data[i], block->data[i + 1]);
}
furi_string_cat_printf(str, "\n");
}
static void nfc_render_felica_block_data_simple(const FelicaBlock* block, FuriString* str) {
for(size_t i = 0; i < FELICA_DATA_BLOCK_SIZE; i += 2) {
furi_string_cat_printf(str, "%02X%02X ", block->data[i], block->data[i + 1]);
}
}
static void nfc_render_felica_block(
const FelicaBlock* block,
FuriString* str,
@@ -76,8 +101,13 @@ static void nfc_render_felica_block(
nfc_render_felica_block_data(block, str);
}
void nfc_render_felica_dump(const FelicaData* data, FuriString* str) {
void nfc_more_info_render_felica_lite_dump(const FelicaData* data, FuriString* str) {
FuriString* name = furi_string_alloc();
furi_string_cat_printf(str, "\e#Blocks read:\n");
furi_string_cat_printf(str, "Blocks: %u\n", data->blocks_total);
for(size_t i = 0; i < 14; i++) {
furi_string_printf(name, "S_PAD%d", i);
uint8_t suf_cnt = 18;
@@ -105,3 +135,70 @@ void nfc_render_felica_dump(const FelicaData* data, FuriString* str) {
nfc_render_felica_block(&data->data.fs.state, str, "STATE", 20, 21);
nfc_render_felica_block(&data->data.fs.crc_check, str, "CRC_CHCK", 15, 17);
}
void nfc_more_info_render_felica_dir(const FelicaData* data, FuriString* str) {
const size_t area_count = simple_array_get_count(data->areas);
const size_t service_count = simple_array_get_count(data->services);
furi_string_cat_printf(str, "\e#Directory Tree:\n");
if(area_count == 0 || service_count == 0) {
furi_string_cat_printf(str, "No services or areas found.\n");
} else {
furi_string_cat_printf(
str, "%zu areas found.\n%zu services found.\n\n", area_count, service_count);
furi_string_cat_printf(
str, "::: ... are readable services\n||| ... are locked services\n");
}
felica_write_directory_tree(data, str);
}
void nfc_more_info_render_felica_blocks(
const FelicaData* data,
FuriString* str,
const uint16_t service_code_key) {
furi_string_cat_printf(str, "\n");
if(data->workflow_type == FelicaLite) {
furi_string_cat_printf(str, "Blocks: %u\n", data->blocks_total);
FuriString* name = furi_string_alloc();
for(size_t i = 0; i < 14; i++) {
furi_string_printf(name, "S_PAD%d", i);
uint8_t suf_cnt = 18;
if(i == 1) {
suf_cnt = 19;
} else if((i == 10) || (i == 12) || (i == 13)) {
suf_cnt = 16;
}
nfc_render_felica_block(
&data->data.fs.spad[i], str, furi_string_get_cstr(name), 20, suf_cnt);
}
furi_string_free(name);
nfc_render_felica_block(&data->data.fs.reg, str, "REG", 23, 23);
nfc_render_felica_block(&data->data.fs.rc, str, "RC", 25, 25);
nfc_render_felica_block(&data->data.fs.mac, str, "MAC", 23, 23);
nfc_render_felica_block(&data->data.fs.id, str, "ID", 25, 25);
nfc_render_felica_block(&data->data.fs.d_id, str, "D_ID", 22, 24);
nfc_render_felica_block(&data->data.fs.ser_c, str, "SER_C", 20, 21);
nfc_render_felica_block(&data->data.fs.sys_c, str, "SYS_C", 20, 21);
nfc_render_felica_block(&data->data.fs.ckv, str, "CKV", 23, 23);
nfc_render_felica_block(&data->data.fs.ck, str, "CK", 25, 25);
nfc_render_felica_block(&data->data.fs.mc, str, "MC", 25, 24);
nfc_render_felica_block(&data->data.fs.wcnt, str, "WCNT", 22, 20);
nfc_render_felica_block(&data->data.fs.mac_a, str, "MAC_A", 20, 20);
nfc_render_felica_block(&data->data.fs.state, str, "STATE", 20, 21);
nfc_render_felica_block(&data->data.fs.crc_check, str, "CRC_CHCK", 15, 17);
} else if(data->workflow_type == FelicaStandard) {
uint32_t public_blocks_count = simple_array_get_count(data->public_blocks);
for(size_t i = 0; i < public_blocks_count; i++) {
FelicaPublicBlock* public_block = simple_array_get(data->public_blocks, i);
if(public_block->service_code != service_code_key) {
continue; // Skip blocks not matching the requested service code
}
furi_string_cat_printf(str, "-----Block 0x%02X-----\n", public_block->block_idx);
nfc_render_felica_block_data_simple(&public_block->block, str);
furi_string_cat_printf(str, "\n");
}
}
}

View File

@@ -14,9 +14,16 @@ void nfc_render_felica_info(
NfcProtocolFormatType format_type,
FuriString* str);
void nfc_render_felica_dump(const FelicaData* data, FuriString* str);
void nfc_more_info_render_felica_lite_dump(const FelicaData* data, FuriString* str);
void nfc_render_felica_idm(
const FelicaData* data,
NfcProtocolFormatType format_type,
FuriString* str);
void nfc_more_info_render_felica_dir(const FelicaData* data, FuriString* str);
void nfc_more_info_render_felica_blocks(
const FelicaData* data,
FuriString* str,
const uint16_t service_code_key);

View File

@@ -1,215 +0,0 @@
#include <furi.h>
#include <furi_hal.h>
#include <cli/cli_main_commands.h>
#include <lib/toolbox/args.h>
#include <lib/toolbox/hex.h>
#include <lib/toolbox/bit_buffer.h>
#include <lib/nfc/nfc_poller.h>
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h>
#include <lib/nfc/protocols/iso14443_4b/iso14443_4b_poller.h>
#include <toolbox/pipe.h>
#include <furi_hal_nfc.h>
#define FLAG_EVENT (1 << 10)
#define NFC_MAX_BUFFER_SIZE (256)
#define NFC_BASE_PROTOCOL_MAX (2)
#define POLLER_DONE (1 << 0)
#define POLLER_ERR (1 << 1)
static NfcProtocol BASE_PROTOCOL[NFC_BASE_PROTOCOL_MAX] = {
NfcProtocolIso14443_4a,
NfcProtocolIso14443_4b};
typedef struct ApduContext {
BitBuffer* tx_buffer;
BitBuffer* rx_buffer;
bool ready;
FuriThreadId thread_id;
} ApduContext;
static void nfc_cli_print_usage(void) {
printf("Usage:\r\n");
printf("nfc <cmd>\r\n");
printf("Cmd list:\r\n");
printf("\tapdu\t - Send APDU and print response \r\n");
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
printf("\tfield\t - turn field on\r\n");
}
}
static void nfc_cli_field(PipeSide* pipe, FuriString* args) {
UNUSED(args);
// Check if nfc worker is not busy
if(furi_hal_nfc_is_hal_ready() != FuriHalNfcErrorNone) {
printf("NFC chip failed to start\r\n");
return;
}
furi_hal_nfc_acquire();
furi_hal_nfc_low_power_mode_stop();
furi_hal_nfc_poller_field_on();
printf("Field is on. Don't leave device in this mode for too long.\r\n");
printf("Press Ctrl+C to abort\r\n");
while(!cli_is_pipe_broken_or_is_etx_next_char(pipe)) {
furi_delay_ms(50);
}
furi_hal_nfc_low_power_mode_start();
furi_hal_nfc_release();
}
static NfcCommand trx_callback(NfcGenericEvent event, void* context) {
furi_check(context);
ApduContext* apdu_context = (ApduContext*)context;
if(apdu_context->ready) {
apdu_context->ready = false;
if(NfcProtocolIso14443_4a == event.protocol) {
Iso14443_4aError err = iso14443_4a_poller_send_block(
event.instance, apdu_context->tx_buffer, apdu_context->rx_buffer);
if(Iso14443_4aErrorNone == err) {
furi_thread_flags_set(apdu_context->thread_id, POLLER_DONE);
return NfcCommandContinue;
} else {
furi_thread_flags_set(apdu_context->thread_id, POLLER_ERR);
return NfcCommandStop;
}
} else if(NfcProtocolIso14443_4b == event.protocol) {
Iso14443_4bError err = iso14443_4b_poller_send_block(
event.instance, apdu_context->tx_buffer, apdu_context->rx_buffer);
if(Iso14443_4bErrorNone == err) {
furi_thread_flags_set(apdu_context->thread_id, POLLER_DONE);
return NfcCommandContinue;
} else {
furi_thread_flags_set(apdu_context->thread_id, POLLER_ERR);
return NfcCommandStop;
}
} else {
// should never reach here
furi_crash("Unknown protocol");
}
} else {
furi_delay_ms(100);
}
return NfcCommandContinue;
}
static void nfc_cli_apdu(PipeSide* pipe, FuriString* args) {
UNUSED(pipe);
Nfc* nfc = NULL;
NfcPoller* poller = NULL;
FuriString* data = furi_string_alloc();
uint8_t* req_buffer = NULL;
uint8_t* resp_buffer = NULL;
size_t apdu_size = 0;
size_t resp_size = 0;
NfcProtocol current_protocol = NfcProtocolInvalid;
do {
if(0 == args_get_first_word_length(args)) {
printf(
"Use like `nfc apdu 00a404000e325041592e5359532e444446303100 00a4040008a0000003010102` \r\n");
break;
}
nfc = nfc_alloc();
printf("detecting tag\r\n");
for(int i = 0; i < NFC_BASE_PROTOCOL_MAX; i++) {
poller = nfc_poller_alloc(nfc, BASE_PROTOCOL[i]);
bool is_detected = nfc_poller_detect(poller);
nfc_poller_free(poller);
if(is_detected) {
current_protocol = BASE_PROTOCOL[i];
printf("detected tag:%d\r\n", BASE_PROTOCOL[i]);
break;
}
}
if(NfcProtocolInvalid == current_protocol) {
nfc_free(nfc);
printf("Can not find any tag\r\n");
break;
}
poller = nfc_poller_alloc(nfc, current_protocol);
ApduContext* apdu_context = malloc(sizeof(ApduContext));
apdu_context->tx_buffer = bit_buffer_alloc(NFC_MAX_BUFFER_SIZE);
apdu_context->rx_buffer = bit_buffer_alloc(NFC_MAX_BUFFER_SIZE);
apdu_context->ready = false;
apdu_context->thread_id = furi_thread_get_current_id();
nfc_poller_start(poller, trx_callback, apdu_context);
while(args_read_string_and_trim(args, data)) {
bit_buffer_reset(apdu_context->tx_buffer);
bit_buffer_reset(apdu_context->rx_buffer);
apdu_size = furi_string_size(data) / 2;
req_buffer = malloc(apdu_size);
hex_chars_to_uint8(furi_string_get_cstr(data), req_buffer);
printf("Sending APDU:%s to Tag\r\n", furi_string_get_cstr(data));
bit_buffer_copy_bytes(apdu_context->tx_buffer, req_buffer, apdu_size);
apdu_context->ready = true;
uint32_t flags = furi_thread_flags_wait(POLLER_DONE, FuriFlagWaitAny, 3000);
if(0 == (flags & POLLER_DONE)) {
printf("Error or Timeout");
free(req_buffer);
break;
}
furi_assert(apdu_context->ready == false);
resp_size = bit_buffer_get_size_bytes(apdu_context->rx_buffer) * 2;
if(!resp_size) {
printf("No response\r\n");
free(req_buffer);
continue;
}
resp_buffer = malloc(resp_size);
uint8_to_hex_chars(
bit_buffer_get_data(apdu_context->rx_buffer), resp_buffer, resp_size);
resp_buffer[resp_size] = 0;
printf("Response: %s\r\n", resp_buffer);
free(req_buffer);
free(resp_buffer);
}
nfc_poller_stop(poller);
nfc_poller_free(poller);
nfc_free(nfc);
bit_buffer_free(apdu_context->tx_buffer);
bit_buffer_free(apdu_context->rx_buffer);
free(apdu_context);
} while(false);
furi_string_free(data);
}
static void execute(PipeSide* pipe, FuriString* args, void* context) {
UNUSED(context);
FuriString* cmd;
cmd = furi_string_alloc();
do {
if(!args_read_string_and_trim(args, cmd)) {
nfc_cli_print_usage();
break;
}
if(furi_string_cmp_str(cmd, "apdu") == 0) {
nfc_cli_apdu(pipe, args);
break;
}
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
if(furi_string_cmp_str(cmd, "field") == 0) {
nfc_cli_field(pipe, args);
break;
}
}
nfc_cli_print_usage();
} while(false);
furi_string_free(cmd);
}
CLI_COMMAND_INTERFACE(nfc, execute, CliCommandFlagDefault, 1024, CLI_APPID);

View File

@@ -0,0 +1,678 @@
// https://sega.bsnk.me/allnet/amusement_ic/
#include "nfc_supported_card_plugin.h"
#include <flipper_application.h>
#include <nfc/protocols/felica/felica.h>
#include <bit_lib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TAG "Amusement IC"
#define N_TABLES 8
#define ITERATION_ADD 5
static const uint8_t s_box[9][256] = {
{0x24, 0x3c, 0xba, 0x36, 0xe3, 0x85, 0xa4, 0xd0, 0x93, 0x43, 0x73, 0xb9, 0x70, 0x6e, 0xc9,
0xf1, 0x10, 0x0e, 0x9b, 0x2c, 0x97, 0xe7, 0x0b, 0x63, 0x6c, 0x29, 0x20, 0xfe, 0x86, 0xf3,
0xe1, 0xf5, 0xf6, 0x9f, 0xb6, 0x16, 0x04, 0x7b, 0x8f, 0xab, 0xb1, 0x39, 0x2a, 0x1b, 0xeb,
0x5c, 0xa8, 0xac, 0x38, 0x11, 0x12, 0x5f, 0x89, 0x3e, 0x7d, 0xca, 0xec, 0x53, 0xdb, 0x6d,
0x1e, 0xd2, 0x81, 0x78, 0x96, 0x46, 0xff, 0xf9, 0x54, 0x1c, 0x28, 0x7a, 0x4f, 0xd3, 0xc0,
0xdc, 0xc1, 0x6a, 0xf2, 0xbc, 0xcb, 0x57, 0xfd, 0x4a, 0xe4, 0xf0, 0xb2, 0xc7, 0x95, 0x40,
0x62, 0x52, 0x41, 0xe2, 0xad, 0x49, 0xa6, 0xb5, 0x1f, 0x02, 0xc8, 0xda, 0x92, 0xe5, 0xb0,
0xc5, 0x64, 0x76, 0x48, 0x21, 0xde, 0x0f, 0x45, 0x58, 0x4c, 0xdf, 0xa7, 0x84, 0xcf, 0xd5,
0x15, 0x4e, 0x27, 0x80, 0x6b, 0x7f, 0xfc, 0x44, 0x71, 0x47, 0x22, 0xdd, 0x30, 0x0c, 0xa1,
0x3b, 0xe0, 0x37, 0xa0, 0x35, 0x23, 0x90, 0x32, 0x74, 0xbf, 0x8d, 0xc2, 0xea, 0xd6, 0x50,
0xbb, 0xd9, 0xc4, 0x83, 0xb4, 0x31, 0x68, 0x55, 0x8b, 0x5d, 0xf4, 0x72, 0x18, 0xb7, 0xef,
0xf7, 0x98, 0x2d, 0x01, 0x03, 0x61, 0xcc, 0x0a, 0x8e, 0x13, 0x00, 0xe8, 0x14, 0xaf, 0x09,
0x51, 0x75, 0xa5, 0x2f, 0x1d, 0x0d, 0x65, 0x8a, 0xcd, 0x66, 0x07, 0x3d, 0x05, 0xd8, 0x4b,
0xe9, 0x9d, 0x99, 0x7c, 0x91, 0xd1, 0xb8, 0x19, 0xc3, 0x2b, 0x42, 0x69, 0x88, 0xc6, 0x79,
0x17, 0x3a, 0x4d, 0x5b, 0xa2, 0x5a, 0xfb, 0x25, 0x3f, 0xbd, 0xf8, 0xed, 0xce, 0xe6, 0x87,
0xa3, 0x26, 0xa9, 0x2e, 0xbe, 0x94, 0x08, 0x7e, 0x67, 0x60, 0x8c, 0x9c, 0x5e, 0x6f, 0xb3,
0x9e, 0x06, 0xfa, 0x82, 0xae, 0xee, 0x59, 0x77, 0xd7, 0x1a, 0x9a, 0x34, 0xd4, 0x56, 0xaa,
0x33},
{0xf9, 0x81, 0x7c, 0x00, 0xb9, 0x30, 0x37, 0xd5, 0x90, 0x51, 0x6e, 0xf0, 0xb2, 0x06, 0xfb,
0xcd, 0x39, 0x14, 0x5f, 0xf8, 0x1b, 0xc7, 0x4a, 0x82, 0x70, 0x8c, 0x92, 0x1d, 0xea, 0xc3,
0x4e, 0xc8, 0x12, 0xe6, 0xb6, 0x10, 0xd3, 0x2f, 0x95, 0x84, 0x25, 0x42, 0xa5, 0x72, 0xe5,
0x8f, 0x55, 0xef, 0x86, 0xa2, 0x53, 0xae, 0xed, 0x26, 0x20, 0x47, 0xde, 0x78, 0x68, 0x28,
0xe4, 0x45, 0xcc, 0x35, 0xbc, 0x3e, 0xbb, 0x8e, 0xc0, 0xbe, 0x34, 0xaf, 0xa6, 0x09, 0x64,
0x01, 0x7b, 0x44, 0xf5, 0xf3, 0xb1, 0x3f, 0xa4, 0xb3, 0x32, 0x80, 0xc9, 0x4f, 0x6f, 0x40,
0xbf, 0x21, 0x4c, 0x74, 0xe1, 0x11, 0x5e, 0xeb, 0x3d, 0x71, 0x2e, 0x9d, 0x62, 0x75, 0xd4,
0x16, 0x48, 0x77, 0x13, 0x67, 0xad, 0x6b, 0x5d, 0x07, 0x87, 0xf7, 0xa8, 0x9a, 0x59, 0x76,
0x8b, 0x9b, 0x1e, 0xd8, 0xee, 0xaa, 0xe9, 0x99, 0x2d, 0xc5, 0x97, 0xf1, 0xa7, 0x83, 0xfc,
0xca, 0xdc, 0xba, 0xb8, 0x4b, 0xe8, 0x89, 0x17, 0x05, 0x0b, 0xa0, 0x65, 0x23, 0xd1, 0xce,
0x36, 0x03, 0xd7, 0xe0, 0xf2, 0x91, 0xdf, 0x0e, 0x9c, 0x2c, 0x0d, 0x66, 0xd6, 0x73, 0x58,
0x5c, 0xb4, 0x0a, 0x4d, 0xc2, 0x3b, 0xd2, 0xb7, 0xe2, 0xac, 0x33, 0xdb, 0x60, 0x27, 0xbd,
0x56, 0x43, 0x24, 0x04, 0x02, 0x8d, 0x7d, 0x7f, 0xf6, 0xb5, 0xf4, 0xfd, 0xdd, 0x5b, 0xec,
0x79, 0xc4, 0x22, 0x1f, 0xa1, 0x88, 0x54, 0xe7, 0x19, 0x98, 0x94, 0x7e, 0x31, 0xa3, 0x29,
0xe3, 0x5a, 0xcb, 0x6a, 0xb0, 0xcf, 0x6d, 0x93, 0x6c, 0x7a, 0x08, 0xa9, 0x3c, 0x1c, 0xd0,
0x63, 0x50, 0xc6, 0x85, 0x38, 0xc1, 0x41, 0x49, 0xab, 0x61, 0x52, 0x2a, 0x9f, 0xd9, 0x18,
0x1a, 0x57, 0x46, 0x2b, 0x69, 0xda, 0xfa, 0xff, 0x0f, 0x15, 0x96, 0x3a, 0x8a, 0xfe, 0x9e,
0x0c},
{0x11, 0xa2, 0x9a, 0xc3, 0x94, 0xa0, 0x51, 0x01, 0x5b, 0xf7, 0xd1, 0x30, 0xee, 0x1f, 0x06,
0xd5, 0x77, 0x67, 0xd3, 0xc1, 0x6e, 0xe7, 0x6a, 0x1a, 0xa4, 0x8e, 0x9f, 0x65, 0x66, 0xd6,
0x0c, 0x2d, 0xc6, 0xe4, 0x69, 0xb7, 0x56, 0x5a, 0x57, 0x60, 0xfc, 0x79, 0x1e, 0xe1, 0x16,
0x52, 0xf0, 0x07, 0xd0, 0xcd, 0xca, 0x78, 0xc9, 0x8b, 0xb3, 0x88, 0x27, 0xf6, 0xe0, 0xc0,
0x84, 0xcf, 0x2f, 0xa3, 0x04, 0x00, 0x80, 0x40, 0xa7, 0xfa, 0x75, 0x9d, 0x4b, 0x89, 0x46,
0x22, 0x28, 0x37, 0x34, 0x13, 0xff, 0x20, 0xb4, 0xda, 0x08, 0x26, 0x6c, 0x8f, 0xf2, 0x5d,
0x7b, 0x73, 0x5c, 0x4c, 0x90, 0x71, 0x41, 0x8a, 0x9e, 0xbb, 0x12, 0xe8, 0x55, 0x6d, 0x2a,
0x25, 0x4a, 0xaf, 0xbd, 0x95, 0x19, 0xa5, 0x31, 0xba, 0xad, 0xd9, 0xc5, 0xa6, 0x6b, 0x83,
0xc4, 0xb6, 0xa9, 0xcc, 0xf9, 0xa1, 0xb1, 0x7a, 0xdc, 0xc7, 0xdb, 0x2e, 0x7e, 0x7f, 0x8d,
0x4e, 0x62, 0x0f, 0x03, 0x39, 0xfd, 0xb8, 0xd4, 0x18, 0xc2, 0xec, 0x61, 0x02, 0x53, 0x1c,
0x3b, 0x7d, 0xbc, 0x1d, 0x59, 0xf5, 0x8c, 0x98, 0xf1, 0x6f, 0x93, 0x85, 0xf8, 0x2c, 0x74,
0xd8, 0x5e, 0x4d, 0x97, 0xab, 0x87, 0x82, 0x0a, 0x21, 0x42, 0x5f, 0x0d, 0x58, 0x33, 0x44,
0x3a, 0xdd, 0xe9, 0xfb, 0x50, 0x47, 0xc8, 0xd7, 0x81, 0x9b, 0xe5, 0x1b, 0x17, 0x54, 0x86,
0x2b, 0xef, 0xf4, 0x29, 0x32, 0x0e, 0x7c, 0x23, 0x15, 0xeb, 0xe6, 0x3c, 0x35, 0xe2, 0x96,
0x68, 0x3f, 0x0b, 0x43, 0xce, 0xde, 0x9c, 0xbe, 0x4f, 0x45, 0x72, 0x76, 0xb5, 0x10, 0xe3,
0xdf, 0x92, 0xd2, 0xa8, 0x48, 0x91, 0xb9, 0xac, 0xbf, 0x49, 0xb2, 0x99, 0x64, 0x70, 0xea,
0xf3, 0x3e, 0x63, 0x14, 0xae, 0xed, 0xaa, 0x24, 0xfe, 0x3d, 0xcb, 0xb0, 0x09, 0x38, 0x36,
0x05},
{0xc4, 0xf0, 0x28, 0x49, 0x55, 0x4a, 0xf5, 0xfd, 0x75, 0xaf, 0x20, 0x69, 0xc8, 0x43, 0x86,
0x6b, 0xc9, 0xa8, 0xc6, 0x54, 0x4c, 0xdd, 0x02, 0x5b, 0xe8, 0x9b, 0x59, 0x77, 0x34, 0xd7,
0xc0, 0x51, 0x5f, 0xf3, 0x7e, 0xd4, 0xf1, 0x90, 0x81, 0xce, 0x19, 0x8a, 0x78, 0x33, 0xcd,
0x97, 0x8c, 0xd6, 0x6a, 0x4b, 0x8f, 0xa4, 0x80, 0xdc, 0x1a, 0x17, 0x14, 0xc5, 0x07, 0x87,
0x66, 0xad, 0x9d, 0x85, 0xb2, 0xf8, 0x8d, 0x98, 0xdf, 0x3e, 0x1f, 0x3f, 0x64, 0x58, 0x40,
0xac, 0x6c, 0x5c, 0x08, 0x5d, 0x53, 0xba, 0xff, 0xd2, 0xa9, 0x2c, 0x25, 0xab, 0xe4, 0x32,
0x38, 0x9a, 0xf9, 0xcb, 0xc2, 0x91, 0x67, 0xd0, 0xe3, 0x22, 0x29, 0x2d, 0x92, 0x48, 0xb4,
0x0e, 0x99, 0x05, 0xa3, 0x6f, 0x0a, 0x15, 0xef, 0xd5, 0x10, 0x4f, 0xd8, 0xf6, 0x00, 0xe6,
0x46, 0x2b, 0x31, 0x57, 0x83, 0x94, 0xae, 0x03, 0xeb, 0x8e, 0x13, 0x24, 0xa1, 0x1e, 0x5e,
0xd1, 0x26, 0xd9, 0xa7, 0xca, 0x36, 0x6e, 0x71, 0x37, 0xee, 0xb1, 0xfa, 0x7c, 0x76, 0x06,
0xb8, 0x23, 0xbe, 0x21, 0xbc, 0x9e, 0xc1, 0xda, 0x7a, 0x3b, 0x62, 0x27, 0x7b, 0xb0, 0xe5,
0x63, 0x18, 0x82, 0x7f, 0x0f, 0xed, 0xa0, 0x2a, 0x9c, 0xbf, 0x11, 0xbd, 0xe0, 0x88, 0x0d,
0x3a, 0x79, 0x52, 0x56, 0xf7, 0xb6, 0xd3, 0x09, 0x16, 0x1b, 0x70, 0xb3, 0x42, 0x60, 0x2f,
0x1c, 0xa2, 0x9f, 0x72, 0x12, 0x45, 0x47, 0xbb, 0xe1, 0x0b, 0x01, 0x8b, 0x1d, 0xde, 0xec,
0x0c, 0x4e, 0xe9, 0xcf, 0xcc, 0x95, 0x74, 0xf4, 0xa5, 0x93, 0xc7, 0xdb, 0x4d, 0xfb, 0x35,
0x65, 0xfe, 0xe7, 0x39, 0xb5, 0xea, 0x96, 0xc3, 0x04, 0x41, 0x44, 0x84, 0xfc, 0x6d, 0x30,
0xe2, 0xf2, 0x68, 0xb7, 0x89, 0x7d, 0x73, 0x3d, 0xb9, 0x5a, 0x50, 0xa6, 0x3c, 0x61, 0xaa,
0x2e},
{0x1a, 0x59, 0x9c, 0xad, 0xc8, 0xe4, 0x11, 0x54, 0xed, 0x37, 0x0f, 0x3a, 0xe6, 0x5f, 0x3c,
0x4b, 0xb8, 0x15, 0x89, 0xb1, 0xe8, 0xda, 0x69, 0x77, 0x91, 0x56, 0x8b, 0xdb, 0x06, 0x24,
0xcf, 0x18, 0xf8, 0xb0, 0x87, 0xdf, 0x8c, 0x35, 0xcb, 0x86, 0x53, 0x9d, 0xa4, 0x66, 0x4a,
0x7a, 0x71, 0x0a, 0x48, 0x38, 0xff, 0xdc, 0x83, 0x20, 0xce, 0x98, 0x32, 0xf6, 0xd7, 0xaf,
0x70, 0x5e, 0x73, 0x8a, 0x14, 0x72, 0x1e, 0xc1, 0x29, 0x79, 0x07, 0x08, 0xe9, 0x43, 0x46,
0xd0, 0x2f, 0xde, 0x2a, 0x4f, 0x3d, 0x2c, 0x50, 0xb3, 0x75, 0xfc, 0x0b, 0x64, 0xae, 0x31,
0x7b, 0x61, 0xa5, 0x30, 0xa0, 0x93, 0xd2, 0xbe, 0xc2, 0x55, 0xba, 0x6c, 0xf3, 0x62, 0x68,
0xfd, 0xac, 0x3b, 0x95, 0x49, 0x1f, 0x6b, 0xb4, 0x85, 0xf7, 0xa6, 0x03, 0xec, 0x6e, 0x9a,
0x81, 0x09, 0x0c, 0x6a, 0xee, 0x9e, 0x4e, 0xf0, 0xab, 0x2d, 0x7e, 0xa1, 0xe1, 0xbb, 0xc9,
0xbc, 0x41, 0xf2, 0xfa, 0x2e, 0x1b, 0xdd, 0x27, 0x34, 0xd3, 0x60, 0x04, 0xb5, 0x01, 0xd6,
0x40, 0xf9, 0xd5, 0x02, 0x47, 0xd9, 0xd8, 0xe0, 0x8f, 0x2b, 0xfb, 0xb7, 0xc5, 0xeb, 0x57,
0x16, 0xe5, 0x78, 0x8d, 0xf4, 0x00, 0xcd, 0x82, 0x39, 0xc6, 0x96, 0xbd, 0xbf, 0xe3, 0x36,
0x45, 0x0e, 0x26, 0x1d, 0x63, 0xe7, 0x84, 0x3e, 0xb6, 0x9b, 0x22, 0xa3, 0xf5, 0x8e, 0x23,
0x6d, 0xc3, 0x99, 0x7d, 0x90, 0x97, 0x10, 0x25, 0x80, 0xd4, 0x4c, 0xe2, 0x74, 0xcc, 0xb2,
0x5c, 0x33, 0xc7, 0xea, 0x05, 0x12, 0x3f, 0x51, 0xa8, 0xfe, 0xf1, 0x1c, 0x42, 0xca, 0xd1,
0x76, 0x28, 0x6f, 0x92, 0xa7, 0x67, 0xa9, 0x19, 0xa2, 0x44, 0x5b, 0xaa, 0xef, 0x58, 0x7f,
0x21, 0xc0, 0x88, 0x65, 0xb9, 0x5d, 0x4d, 0x0d, 0x5a, 0xc4, 0x13, 0x52, 0x7c, 0x9f, 0x94,
0x17},
{0xc7, 0x2b, 0x82, 0x61, 0x5b, 0xd0, 0x96, 0x84, 0xd3, 0x4a, 0x70, 0xa1, 0x9b, 0x59, 0x33,
0x9f, 0xc0, 0x20, 0x14, 0x53, 0x29, 0x17, 0xc5, 0x0b, 0xc9, 0x7b, 0x97, 0x02, 0x0d, 0x3a,
0x1e, 0x7c, 0x3f, 0x6b, 0x52, 0xe8, 0x75, 0x3d, 0xf6, 0xe4, 0x0c, 0x8a, 0x4f, 0xc3, 0x5f,
0x26, 0x65, 0x73, 0x31, 0x23, 0x28, 0x48, 0x74, 0xaa, 0xa7, 0x36, 0x09, 0xb1, 0xe2, 0x91,
0x04, 0x51, 0x22, 0xfc, 0x08, 0xa6, 0x05, 0xa4, 0xf1, 0x12, 0x1c, 0x19, 0xeb, 0x40, 0x37,
0xc2, 0xa0, 0x41, 0x1d, 0xd4, 0xdc, 0x07, 0x43, 0x8f, 0x47, 0xaf, 0xd1, 0x2e, 0x98, 0xab,
0x01, 0xba, 0xf0, 0x66, 0x68, 0xac, 0xf9, 0xe7, 0x69, 0xb6, 0xcb, 0x8d, 0x78, 0x87, 0x15,
0x03, 0xd5, 0xdf, 0xa3, 0x1a, 0x9d, 0x6a, 0xea, 0x2f, 0x94, 0x4e, 0x9e, 0x42, 0xd7, 0xb8,
0x38, 0x92, 0xd8, 0xbb, 0xde, 0xdd, 0x9a, 0xbc, 0xb0, 0x4c, 0x79, 0xf4, 0x58, 0x3e, 0xe9,
0x83, 0x81, 0xff, 0xe3, 0x55, 0xfd, 0x5d, 0xb2, 0xef, 0x9c, 0x6d, 0x54, 0x99, 0x60, 0xda,
0x3b, 0xec, 0xfa, 0x11, 0xd6, 0xc4, 0x2a, 0xed, 0x4b, 0xae, 0x13, 0xbf, 0xb9, 0x06, 0x8b,
0xe0, 0x1f, 0x7f, 0x5a, 0xad, 0x90, 0x39, 0x0f, 0xf3, 0xbd, 0x46, 0x6c, 0x2c, 0xf2, 0xf8,
0xfe, 0xd9, 0xe6, 0x72, 0x0e, 0x89, 0xbe, 0x5e, 0xc6, 0xa8, 0xcf, 0xf5, 0x57, 0x7e, 0x8c,
0xb7, 0xe1, 0x88, 0x7a, 0xc8, 0x1b, 0x18, 0xdb, 0x6f, 0x35, 0xd2, 0x16, 0x32, 0x64, 0x63,
0xa5, 0x8e, 0xf7, 0xb4, 0x76, 0x95, 0x86, 0x7d, 0xe5, 0xa9, 0x5c, 0x85, 0x00, 0xc1, 0x93,
0x4d, 0xfb, 0x30, 0xa2, 0xb5, 0x25, 0x34, 0x10, 0x77, 0x3c, 0x67, 0x71, 0x2d, 0x44, 0x45,
0x56, 0x0a, 0x50, 0xb3, 0xcd, 0x62, 0x80, 0xce, 0x21, 0x49, 0x24, 0xca, 0xee, 0x27, 0x6e,
0xcc},
{0xa5, 0xb0, 0x6d, 0xb2, 0x51, 0x55, 0x1f, 0xbf, 0x3e, 0x8d, 0xdd, 0x19, 0x92, 0xea, 0x1b,
0xbd, 0x32, 0x65, 0x29, 0xa4, 0x89, 0x67, 0xb1, 0x90, 0x68, 0x00, 0xda, 0x0d, 0xa6, 0x59,
0x54, 0xf2, 0x10, 0x14, 0x2f, 0x45, 0xe0, 0x3b, 0x23, 0xfa, 0xd7, 0x50, 0xac, 0x93, 0xe6,
0x43, 0x01, 0x0a, 0x5c, 0x78, 0x70, 0x98, 0x46, 0xa9, 0x7b, 0xf3, 0x95, 0x07, 0x2a, 0xd3,
0x74, 0xb3, 0x3f, 0xa3, 0x60, 0x82, 0x39, 0x4e, 0x34, 0x48, 0xc0, 0x1c, 0xf6, 0xc2, 0x91,
0x64, 0x4d, 0x3c, 0xf8, 0x9d, 0x35, 0x9a, 0x94, 0xe3, 0x7a, 0xf0, 0xf9, 0x6e, 0xb9, 0x12,
0xb8, 0x04, 0x2d, 0x02, 0x28, 0x13, 0x85, 0x72, 0x80, 0x87, 0xdb, 0x2b, 0xf1, 0x4f, 0x26,
0xa2, 0xe1, 0x49, 0x7e, 0x9c, 0xcc, 0xa7, 0xb6, 0xa0, 0xd0, 0x9b, 0x36, 0x77, 0xad, 0x8b,
0x6b, 0x4a, 0x03, 0x1d, 0x05, 0x8a, 0x06, 0x4b, 0xaf, 0xe5, 0x31, 0xb5, 0xd4, 0xc6, 0x0c,
0x66, 0xba, 0x83, 0xfd, 0x09, 0x0f, 0xae, 0x71, 0xb7, 0x6f, 0xdc, 0x41, 0xe8, 0x17, 0x8e,
0x40, 0x7f, 0x62, 0x30, 0xff, 0xa8, 0x84, 0x25, 0xfb, 0x16, 0xce, 0x37, 0x44, 0xab, 0x99,
0x1e, 0xeb, 0x18, 0x3a, 0x47, 0xf7, 0x5f, 0x81, 0xcf, 0xed, 0x58, 0x2c, 0x6c, 0xfe, 0x9e,
0x57, 0x53, 0x97, 0x20, 0xca, 0x79, 0x1a, 0x5a, 0x88, 0xf5, 0x69, 0x9f, 0xe7, 0xd9, 0x0e,
0xbe, 0x42, 0xdf, 0x56, 0xe4, 0x4c, 0x22, 0xaa, 0x73, 0x0b, 0x15, 0xc5, 0xee, 0xfc, 0xc7,
0xd6, 0xcb, 0xcd, 0x8c, 0xe2, 0x76, 0x21, 0xe9, 0xd1, 0xec, 0xc8, 0x7d, 0xd8, 0x8f, 0x61,
0x7c, 0x2e, 0xbc, 0xde, 0xb4, 0x75, 0xd2, 0xc4, 0x63, 0x3d, 0xa1, 0x5e, 0x5d, 0x6a, 0x08,
0x24, 0xc9, 0x27, 0xbb, 0xef, 0x33, 0x86, 0x5b, 0xd5, 0x38, 0x52, 0x11, 0xf4, 0xc3, 0x96,
0xc1},
{0x34, 0x5a, 0xdb, 0x2c, 0x59, 0x57, 0x12, 0x2b, 0x30, 0xc2, 0xa0, 0x92, 0xbf, 0xed, 0xbc,
0x45, 0xde, 0x27, 0x9b, 0x96, 0xd3, 0xe6, 0xc5, 0xeb, 0xd8, 0x24, 0x4b, 0xa4, 0x21, 0xcc,
0xa8, 0xd6, 0xce, 0x3c, 0xba, 0xb1, 0x09, 0xe0, 0xd7, 0x32, 0x66, 0x4a, 0x83, 0x1d, 0x19,
0xca, 0x89, 0x67, 0x0f, 0x42, 0x07, 0x71, 0xe9, 0xbb, 0x44, 0x5e, 0x85, 0x17, 0xc4, 0xae,
0x9c, 0x3f, 0x6b, 0x78, 0xe7, 0x6d, 0x02, 0xa7, 0xfe, 0x33, 0xe3, 0xd9, 0x0a, 0x7d, 0xea,
0xf1, 0x18, 0x87, 0x7b, 0x62, 0x03, 0x79, 0x52, 0x23, 0x00, 0x3e, 0x25, 0xb9, 0xdd, 0x16,
0x68, 0x3b, 0x1f, 0x90, 0xa6, 0x2a, 0xc6, 0x29, 0x91, 0x8a, 0x9d, 0xef, 0x1e, 0x84, 0x76,
0xee, 0x4f, 0x39, 0xfb, 0x11, 0x1b, 0x0b, 0x93, 0xad, 0x49, 0xb7, 0x05, 0xe1, 0x4d, 0xcb,
0xe8, 0x38, 0xd0, 0x55, 0xf2, 0xf7, 0x0d, 0x8b, 0x65, 0xcd, 0xfa, 0xd4, 0x9e, 0xc9, 0x81,
0x4e, 0x14, 0xc8, 0x26, 0xb6, 0xf9, 0xaa, 0xf5, 0x80, 0xf8, 0x6a, 0x98, 0xab, 0x58, 0xf3,
0xb8, 0x8e, 0xb5, 0x97, 0x43, 0x72, 0xa2, 0xda, 0x64, 0xc1, 0x40, 0x5c, 0x13, 0xb0, 0xe5,
0xbd, 0x08, 0x9f, 0x1c, 0x7e, 0x8f, 0x06, 0xbe, 0x6e, 0x50, 0x1a, 0x2f, 0x37, 0xa1, 0xfc,
0x10, 0x2e, 0x70, 0x53, 0x04, 0x20, 0x63, 0x48, 0xe2, 0xfd, 0x6f, 0x7a, 0x75, 0x61, 0xb3,
0x35, 0x3a, 0xcf, 0x5b, 0x88, 0x82, 0x51, 0xd2, 0x47, 0xa5, 0xa9, 0x74, 0x6c, 0x31, 0x94,
0x7c, 0x9a, 0xc0, 0xec, 0x15, 0x0e, 0x28, 0x01, 0x2d, 0xc3, 0xf0, 0xb4, 0xe4, 0x8d, 0xdc,
0xac, 0x3d, 0x5f, 0x86, 0xc7, 0x95, 0x22, 0xf4, 0xb2, 0xa3, 0x54, 0x46, 0xd1, 0x77, 0x8c,
0x0c, 0xff, 0xaf, 0x56, 0x5d, 0x36, 0x69, 0x7f, 0x99, 0x4c, 0xd5, 0x60, 0x73, 0xdf, 0x41,
0xf6},
{0x04, 0xda, 0x79, 0x63, 0x1e, 0xbd, 0xea, 0xe3, 0x0b, 0x65, 0x25, 0x01, 0xcd, 0xa9, 0x92,
0xed, 0x18, 0x75, 0x57, 0x30, 0x89, 0x03, 0x09, 0x6a, 0xdc, 0xc7, 0x98, 0xa4, 0x50, 0x91,
0x1f, 0xb1, 0x0c, 0x77, 0x85, 0x66, 0xca, 0x12, 0x1c, 0x67, 0xc2, 0xe0, 0x17, 0x83, 0x59,
0x9d, 0xd5, 0x22, 0x82, 0x56, 0xa8, 0x9f, 0xc6, 0x60, 0x48, 0xb3, 0x11, 0xfc, 0xa3, 0x28,
0xfb, 0x06, 0xdd, 0x5d, 0xba, 0x29, 0x7a, 0x4f, 0xe8, 0xfe, 0xe9, 0x10, 0xcb, 0xf3, 0x93,
0x7b, 0x6c, 0x69, 0x54, 0xe7, 0x44, 0xa2, 0x84, 0x1d, 0x8d, 0xce, 0xff, 0xfa, 0x1a, 0x87,
0x90, 0x74, 0xa1, 0xf8, 0x14, 0xaa, 0xbc, 0xc0, 0xcf, 0x31, 0xb4, 0xd9, 0xbf, 0xd8, 0x5e,
0x26, 0x2d, 0xd0, 0xe4, 0x3f, 0x19, 0xd6, 0x8c, 0x2f, 0xab, 0x39, 0x58, 0x72, 0x6e, 0xdf,
0x3b, 0xe6, 0x3c, 0xb6, 0x62, 0x88, 0xde, 0x40, 0xb2, 0x8f, 0x9b, 0x7c, 0x95, 0x43, 0xd1,
0x9e, 0xac, 0x9c, 0xd4, 0x23, 0xe1, 0x0e, 0x4b, 0x53, 0x33, 0x46, 0x20, 0x13, 0x34, 0x1b,
0x97, 0xf7, 0xf1, 0xc3, 0x61, 0x4a, 0x6f, 0x5a, 0x21, 0x7f, 0x70, 0x2e, 0x55, 0x41, 0x05,
0xc5, 0xd7, 0x76, 0xe5, 0x27, 0x15, 0xec, 0x42, 0x5c, 0x4d, 0x78, 0x35, 0x8b, 0xef, 0xd2,
0xee, 0xb5, 0xbe, 0xae, 0x02, 0x3a, 0xd3, 0x5f, 0xc4, 0x24, 0xf0, 0xf9, 0x51, 0x4e, 0xeb,
0x00, 0x0f, 0xfd, 0xaf, 0x3e, 0xc1, 0x9a, 0x52, 0x86, 0x81, 0x80, 0x7e, 0xf6, 0x2b, 0xcc,
0xb9, 0x7d, 0x68, 0xf2, 0xad, 0x99, 0xa7, 0x07, 0x2c, 0x73, 0x38, 0xb0, 0x6b, 0xb7, 0x8e,
0x71, 0xa0, 0xf4, 0x3d, 0xa6, 0x0d, 0x37, 0xdb, 0x0a, 0x47, 0x36, 0x16, 0x96, 0x6d, 0x32,
0x2a, 0x5b, 0xe2, 0x45, 0x94, 0xf5, 0xa5, 0x4c, 0xc8, 0x8a, 0x49, 0x64, 0xbb, 0x08, 0xc9,
0xb8},
};
static const uint8_t access_code_table_1[5][10][100] = {
{
{11, 38, 3, 63, 36, 39, 79, 19, 87, 68, 76, 51, 20, 56, 77, 61, 52, 73, 74, 94,
45, 31, 99, 28, 29, 90, 81, 96, 65, 75, 13, 32, 70, 21, 55, 33, 9, 15, 92, 14,
82, 97, 78, 37, 49, 0, 80, 57, 58, 7, 1, 89, 98, 53, 64, 5, 6, 66, 43, 83,
10, 50, 47, 84, 62, 71, 4, 67, 86, 42, 35, 34, 91, 12, 17, 69, 44, 48, 85, 30,
16, 95, 54, 23, 46, 18, 88, 59, 40, 60, 41, 25, 27, 22, 2, 24, 72, 93, 26, 8},
{28, 58, 74, 34, 6, 14, 16, 46, 87, 24, 59, 57, 23, 88, 75, 65, 79, 38, 82, 13,
49, 99, 8, 94, 15, 19, 0, 96, 41, 95, 45, 35, 1, 91, 2, 52, 42, 76, 90, 84,
20, 54, 4, 63, 25, 5, 47, 85, 93, 12, 51, 81, 71, 69, 22, 17, 36, 64, 77, 80,
98, 53, 44, 18, 10, 68, 97, 61, 56, 70, 92, 27, 40, 9, 60, 7, 72, 21, 43, 3,
33, 32, 50, 26, 67, 62, 39, 48, 73, 86, 29, 89, 37, 78, 55, 66, 31, 30, 11, 83},
{4, 16, 22, 66, 37, 14, 47, 89, 59, 60, 51, 92, 71, 70, 96, 13, 17, 85, 58, 93,
81, 69, 28, 90, 78, 39, 63, 88, 0, 75, 2, 53, 23, 99, 94, 68, 54, 74, 41, 29,
8, 26, 50, 98, 82, 95, 42, 7, 24, 77, 30, 9, 21, 1, 87, 62, 46, 15, 43, 38,
36, 55, 49, 57, 52, 56, 91, 65, 11, 84, 6, 73, 97, 33, 83, 27, 35, 25, 40, 32,
18, 31, 86, 48, 45, 3, 79, 76, 72, 64, 44, 20, 80, 19, 10, 5, 61, 34, 67, 12},
{80, 91, 71, 26, 75, 8, 22, 92, 7, 70, 65, 51, 89, 31, 81, 30, 83, 47, 44, 85,
55, 19, 57, 87, 74, 6, 73, 40, 49, 32, 52, 78, 0, 12, 54, 15, 64, 28, 4, 24,
68, 17, 72, 53, 1, 94, 58, 88, 11, 82, 43, 86, 60, 63, 99, 96, 37, 77, 67, 33,
18, 5, 41, 59, 16, 36, 79, 97, 61, 23, 56, 48, 98, 38, 66, 21, 34, 29, 10, 3,
93, 46, 50, 39, 14, 9, 2, 13, 69, 42, 62, 35, 27, 90, 25, 76, 20, 95, 45, 84},
{48, 16, 54, 47, 9, 39, 81, 91, 33, 53, 63, 21, 50, 85, 97, 90, 49, 36, 84, 74,
51, 76, 37, 67, 59, 58, 23, 20, 65, 82, 7, 77, 12, 31, 46, 1, 5, 14, 3, 30,
94, 95, 2, 34, 70, 89, 40, 87, 43, 79, 64, 57, 68, 26, 56, 44, 88, 61, 0, 24,
71, 18, 60, 4, 80, 96, 27, 15, 6, 13, 98, 29, 28, 25, 72, 93, 55, 75, 52, 66,
11, 8, 86, 45, 22, 83, 17, 10, 35, 38, 73, 99, 62, 41, 19, 69, 78, 92, 42, 32},
{23, 19, 88, 44, 5, 39, 75, 81, 30, 14, 49, 54, 62, 55, 18, 83, 77, 76, 74, 92,
6, 95, 52, 36, 15, 27, 53, 38, 28, 50, 48, 99, 78, 67, 90, 87, 16, 41, 58, 65,
79, 11, 68, 84, 24, 97, 64, 47, 9, 22, 43, 86, 37, 8, 33, 17, 93, 61, 25, 72,
57, 98, 42, 80, 13, 89, 2, 12, 31, 56, 26, 85, 3, 29, 66, 63, 82, 10, 20, 94,
51, 0, 1, 4, 21, 60, 34, 35, 32, 45, 59, 71, 46, 69, 7, 96, 91, 40, 70, 73},
{89, 62, 33, 19, 21, 97, 15, 2, 83, 22, 65, 30, 55, 7, 63, 73, 39, 61, 44, 37,
11, 26, 31, 79, 91, 0, 72, 68, 23, 87, 28, 24, 45, 74, 98, 14, 70, 54, 88, 42,
35, 59, 46, 16, 27, 92, 69, 66, 94, 32, 75, 77, 64, 57, 50, 71, 10, 3, 67, 81,
90, 58, 99, 5, 82, 85, 25, 52, 12, 8, 48, 49, 76, 96, 80, 20, 29, 34, 53, 51,
86, 93, 38, 84, 9, 36, 6, 60, 1, 78, 13, 95, 41, 17, 47, 18, 43, 40, 4, 56},
{40, 68, 88, 47, 43, 28, 67, 23, 42, 99, 48, 3, 12, 27, 79, 25, 6, 55, 19, 80,
81, 62, 83, 69, 39, 73, 36, 30, 44, 82, 70, 52, 31, 34, 58, 74, 54, 2, 13, 45,
93, 49, 75, 72, 29, 87, 5, 7, 17, 76, 90, 63, 84, 51, 59, 35, 20, 97, 22, 65,
57, 66, 78, 86, 11, 9, 71, 98, 53, 56, 15, 46, 92, 0, 26, 21, 32, 38, 77, 91,
4, 24, 61, 85, 94, 1, 96, 64, 8, 41, 33, 50, 18, 60, 10, 16, 37, 95, 14, 89},
{96, 23, 99, 42, 94, 91, 19, 53, 41, 25, 39, 73, 70, 72, 33, 35, 88, 2, 63, 49,
95, 60, 48, 97, 5, 54, 47, 40, 98, 18, 57, 28, 67, 92, 10, 79, 13, 76, 8, 55,
90, 62, 50, 87, 65, 82, 84, 21, 0, 14, 45, 44, 20, 38, 17, 3, 81, 16, 58, 4,
31, 29, 86, 15, 43, 64, 26, 52, 46, 69, 37, 68, 6, 22, 61, 7, 83, 71, 30, 75,
85, 78, 24, 27, 89, 9, 80, 34, 66, 77, 36, 93, 56, 59, 1, 12, 51, 32, 11, 74},
{75, 99, 97, 8, 15, 48, 6, 25, 60, 90, 91, 95, 23, 3, 89, 77, 22, 78, 94, 55,
98, 35, 53, 96, 18, 52, 12, 16, 50, 20, 49, 9, 17, 13, 57, 44, 14, 47, 86, 56,
88, 41, 29, 32, 70, 1, 62, 37, 85, 38, 59, 36, 5, 68, 71, 69, 67, 27, 39, 4,
54, 0, 84, 45, 61, 73, 79, 93, 58, 66, 10, 76, 51, 92, 19, 87, 24, 7, 28, 2,
11, 80, 81, 33, 64, 74, 46, 65, 26, 72, 40, 82, 34, 42, 21, 83, 31, 30, 43, 63},
},
{
{98, 58, 34, 65, 83, 74, 88, 5, 89, 46, 80, 43, 18, 87, 9, 28, 71, 90, 4, 36,
40, 11, 39, 31, 76, 92, 82, 48, 66, 41, 49, 99, 78, 79, 97, 86, 75, 68, 3, 7,
62, 64, 26, 22, 13, 77, 54, 50, 10, 96, 15, 35, 94, 73, 57, 93, 16, 61, 37, 38,
53, 2, 63, 23, 67, 72, 95, 70, 29, 33, 45, 47, 27, 24, 69, 44, 55, 85, 14, 8,
91, 12, 42, 6, 84, 81, 19, 25, 32, 51, 1, 17, 52, 56, 60, 0, 59, 30, 21, 20},
{26, 63, 99, 57, 77, 53, 9, 75, 76, 80, 46, 97, 37, 14, 1, 12, 35, 20, 0, 15,
28, 19, 48, 41, 67, 30, 60, 69, 29, 34, 73, 10, 51, 47, 66, 82, 7, 98, 39, 96,
8, 62, 42, 3, 95, 5, 11, 13, 90, 94, 32, 31, 83, 64, 36, 88, 72, 52, 38, 54,
45, 17, 61, 81, 78, 44, 79, 55, 56, 86, 2, 40, 16, 24, 4, 71, 49, 33, 23, 92,
84, 74, 65, 43, 87, 59, 89, 68, 22, 70, 25, 58, 21, 85, 6, 50, 27, 93, 91, 18},
{22, 31, 11, 72, 37, 70, 90, 60, 24, 56, 28, 69, 54, 85, 5, 98, 1, 76, 59, 20,
86, 32, 61, 49, 63, 42, 74, 53, 84, 3, 95, 23, 15, 36, 4, 81, 6, 21, 19, 27,
17, 83, 30, 45, 25, 65, 10, 50, 80, 66, 46, 75, 93, 82, 78, 99, 97, 68, 8, 13,
58, 94, 41, 14, 48, 52, 29, 79, 91, 89, 9, 55, 57, 2, 39, 92, 0, 44, 18, 62,
73, 96, 34, 88, 16, 77, 71, 43, 33, 40, 7, 12, 87, 51, 26, 47, 64, 35, 67, 38},
{0, 80, 63, 52, 49, 69, 15, 87, 55, 60, 22, 91, 31, 89, 78, 28, 48, 12, 10, 42,
82, 23, 29, 18, 21, 2, 19, 7, 68, 37, 54, 90, 81, 43, 57, 94, 26, 95, 96, 88,
14, 51, 79, 71, 66, 76, 34, 24, 30, 9, 62, 44, 77, 65, 6, 27, 4, 97, 38, 36,
59, 3, 1, 50, 72, 46, 64, 35, 20, 11, 53, 13, 58, 39, 74, 8, 83, 75, 40, 98,
17, 99, 85, 32, 92, 67, 84, 5, 25, 56, 16, 47, 70, 93, 86, 61, 73, 41, 45, 33},
{64, 57, 56, 7, 86, 9, 65, 16, 1, 63, 71, 26, 53, 98, 69, 5, 84, 28, 36, 58,
54, 99, 42, 74, 68, 3, 88, 22, 82, 97, 85, 45, 21, 66, 43, 59, 83, 51, 89, 87,
31, 12, 95, 52, 60, 17, 80, 27, 72, 93, 10, 39, 91, 30, 75, 61, 24, 8, 23, 2,
18, 73, 94, 6, 38, 44, 13, 40, 48, 4, 81, 37, 96, 46, 19, 49, 34, 50, 0, 20,
14, 15, 25, 92, 33, 29, 77, 90, 78, 67, 62, 41, 35, 47, 79, 32, 11, 70, 76, 55},
{24, 99, 28, 52, 64, 3, 63, 16, 15, 87, 47, 6, 59, 98, 13, 78, 57, 81, 56, 94,
0, 37, 70, 73, 65, 83, 90, 92, 60, 82, 67, 40, 74, 34, 91, 2, 33, 85, 21, 54,
12, 31, 84, 55, 53, 49, 36, 25, 39, 20, 29, 93, 51, 76, 42, 96, 71, 86, 95, 66,
45, 19, 8, 58, 22, 69, 80, 79, 89, 17, 61, 18, 88, 38, 72, 75, 48, 30, 7, 10,
11, 35, 23, 9, 26, 4, 62, 44, 1, 43, 46, 14, 41, 77, 97, 27, 68, 32, 5, 50},
{90, 80, 22, 27, 16, 23, 11, 31, 8, 75, 54, 82, 68, 66, 10, 40, 95, 99, 14, 42,
56, 88, 12, 55, 38, 6, 79, 84, 26, 92, 4, 67, 48, 5, 46, 77, 19, 53, 97, 34,
64, 73, 58, 47, 18, 39, 81, 59, 74, 62, 87, 98, 50, 1, 25, 9, 72, 69, 61, 60,
86, 3, 24, 28, 45, 30, 96, 36, 57, 83, 89, 63, 0, 33, 17, 7, 94, 15, 43, 2,
44, 41, 20, 85, 32, 65, 93, 49, 91, 52, 71, 37, 35, 13, 21, 51, 70, 29, 78, 76},
{95, 50, 72, 0, 68, 98, 49, 53, 56, 41, 54, 22, 57, 78, 66, 31, 52, 29, 86, 30,
62, 26, 32, 46, 42, 48, 14, 21, 88, 94, 77, 92, 7, 63, 91, 28, 93, 38, 90, 71,
34, 25, 11, 83, 55, 70, 97, 82, 89, 76, 10, 99, 9, 45, 60, 39, 44, 16, 17, 37,
47, 79, 33, 36, 59, 8, 80, 23, 1, 18, 64, 51, 61, 81, 75, 40, 35, 73, 13, 65,
12, 4, 19, 85, 96, 6, 2, 58, 27, 43, 5, 87, 20, 15, 3, 24, 84, 67, 74, 69},
{38, 33, 29, 88, 49, 47, 60, 69, 75, 12, 44, 89, 35, 45, 23, 30, 21, 28, 65, 86,
81, 40, 93, 97, 67, 2, 94, 9, 61, 92, 90, 32, 62, 36, 58, 51, 37, 73, 50, 63,
39, 43, 16, 71, 46, 91, 13, 7, 20, 72, 41, 59, 70, 48, 10, 52, 5, 34, 76, 15,
87, 6, 98, 18, 57, 77, 24, 79, 27, 19, 3, 0, 8, 95, 74, 64, 42, 4, 68, 31,
84, 54, 99, 25, 1, 80, 85, 26, 83, 11, 56, 55, 78, 66, 82, 14, 53, 22, 17, 96},
{26, 12, 62, 5, 78, 53, 31, 46, 51, 8, 69, 80, 32, 68, 76, 42, 29, 87, 28, 75,
40, 91, 58, 7, 88, 92, 82, 96, 70, 50, 47, 65, 85, 3, 48, 73, 90, 89, 97, 84,
81, 39, 43, 79, 64, 54, 2, 41, 38, 10, 24, 22, 25, 93, 44, 23, 49, 37, 14, 95,
72, 74, 15, 11, 55, 98, 99, 0, 9, 16, 18, 20, 63, 30, 57, 19, 4, 17, 67, 35,
60, 45, 59, 56, 36, 77, 66, 33, 27, 71, 86, 1, 13, 34, 21, 61, 6, 94, 52, 83},
},
{
{10, 87, 64, 11, 25, 97, 93, 18, 99, 38, 17, 72, 50, 23, 90, 57, 98, 12, 29, 46,
19, 77, 42, 15, 73, 52, 53, 89, 35, 20, 9, 41, 79, 13, 95, 48, 85, 54, 61, 94,
0, 74, 62, 40, 78, 68, 24, 28, 86, 21, 59, 14, 82, 84, 92, 58, 34, 31, 6, 16,
75, 81, 43, 47, 7, 3, 2, 37, 67, 8, 66, 22, 30, 32, 71, 44, 70, 1, 36, 26,
33, 63, 80, 83, 91, 76, 56, 39, 69, 55, 96, 60, 27, 49, 65, 88, 5, 4, 51, 45},
{73, 87, 48, 43, 71, 40, 88, 23, 60, 35, 30, 5, 75, 25, 59, 95, 58, 79, 4, 89,
96, 17, 38, 6, 39, 9, 68, 81, 3, 90, 93, 99, 16, 42, 21, 45, 74, 84, 77, 65,
63, 98, 11, 51, 0, 33, 91, 22, 69, 34, 31, 20, 46, 2, 76, 54, 15, 62, 13, 70,
92, 55, 82, 47, 78, 7, 24, 27, 86, 1, 56, 61, 12, 44, 85, 57, 49, 19, 18, 67,
66, 52, 72, 53, 83, 41, 64, 50, 26, 94, 32, 80, 28, 10, 37, 14, 8, 97, 29, 36},
{60, 50, 53, 78, 63, 90, 11, 33, 21, 24, 20, 55, 8, 25, 6, 58, 3, 98, 44, 82,
96, 14, 1, 59, 92, 75, 51, 36, 30, 64, 72, 40, 16, 94, 2, 74, 93, 85, 12, 10,
43, 32, 42, 39, 79, 97, 17, 38, 89, 29, 68, 52, 49, 0, 34, 19, 70, 84, 54, 9,
23, 71, 65, 28, 83, 56, 67, 57, 13, 18, 77, 5, 4, 88, 41, 22, 46, 76, 48, 61,
81, 45, 86, 35, 47, 87, 91, 99, 73, 66, 27, 37, 31, 15, 7, 80, 62, 95, 69, 26},
{49, 73, 63, 66, 38, 35, 32, 96, 87, 26, 36, 92, 58, 6, 17, 47, 29, 84, 11, 18,
67, 45, 78, 4, 52, 81, 59, 30, 91, 54, 56, 89, 48, 46, 20, 79, 1, 62, 9, 53,
10, 95, 88, 34, 40, 80, 41, 64, 83, 55, 43, 7, 24, 61, 77, 5, 14, 93, 16, 21,
85, 39, 76, 50, 57, 65, 37, 60, 28, 19, 2, 97, 42, 33, 13, 68, 90, 25, 69, 99,
98, 74, 3, 8, 12, 15, 82, 71, 31, 23, 75, 27, 51, 94, 86, 0, 44, 22, 72, 70},
{30, 32, 52, 21, 15, 79, 53, 40, 56, 44, 55, 6, 63, 39, 61, 4, 93, 43, 23, 99,
38, 66, 88, 18, 1, 86, 33, 9, 82, 76, 70, 51, 48, 69, 85, 11, 5, 91, 59, 77,
49, 75, 22, 87, 65, 41, 26, 50, 47, 98, 57, 10, 58, 62, 46, 28, 20, 71, 78, 95,
0, 72, 80, 73, 92, 36, 96, 37, 68, 2, 35, 83, 90, 25, 13, 24, 34, 42, 45, 74,
67, 16, 84, 97, 19, 64, 27, 14, 31, 12, 54, 60, 3, 8, 17, 89, 81, 94, 7, 29},
{1, 18, 25, 13, 57, 53, 92, 95, 94, 88, 10, 82, 34, 83, 7, 28, 55, 11, 5, 58,
8, 9, 74, 99, 52, 32, 62, 45, 65, 50, 41, 56, 96, 44, 38, 47, 3, 39, 81, 19,
2, 22, 49, 12, 40, 67, 89, 76, 64, 70, 61, 21, 31, 54, 51, 17, 46, 30, 24, 60,
66, 26, 77, 63, 37, 35, 23, 6, 4, 87, 79, 97, 20, 48, 36, 85, 73, 71, 27, 43,
0, 15, 69, 78, 59, 86, 75, 33, 42, 84, 91, 93, 68, 80, 16, 98, 90, 72, 14, 29},
{30, 16, 10, 67, 83, 81, 42, 94, 35, 59, 22, 5, 53, 61, 1, 96, 68, 45, 95, 66,
36, 37, 97, 87, 77, 93, 86, 33, 49, 13, 43, 14, 0, 34, 7, 2, 41, 21, 31, 32,
76, 55, 62, 72, 90, 71, 38, 23, 46, 12, 51, 19, 6, 44, 52, 48, 73, 24, 29, 74,
85, 99, 80, 28, 47, 9, 39, 84, 64, 57, 75, 98, 88, 26, 17, 63, 27, 56, 69, 8,
54, 25, 91, 78, 3, 50, 65, 70, 11, 4, 92, 60, 40, 58, 15, 89, 82, 18, 79, 20},
{60, 88, 79, 35, 40, 43, 52, 4, 17, 31, 84, 44, 50, 94, 91, 38, 87, 25, 92, 62,
14, 97, 37, 18, 86, 28, 65, 90, 19, 29, 30, 75, 68, 49, 69, 11, 59, 41, 89, 33,
47, 54, 98, 42, 80, 56, 78, 99, 23, 82, 9, 48, 21, 1, 63, 22, 58, 95, 10, 12,
26, 74, 85, 13, 8, 27, 24, 53, 61, 34, 64, 71, 66, 32, 83, 73, 15, 67, 0, 20,
16, 6, 77, 45, 5, 93, 76, 39, 57, 2, 96, 46, 51, 3, 55, 7, 81, 70, 36, 72},
{35, 56, 77, 58, 23, 83, 91, 81, 53, 70, 20, 74, 84, 50, 79, 51, 88, 44, 45, 71,
37, 26, 54, 48, 27, 43, 0, 12, 95, 97, 1, 63, 67, 98, 59, 40, 13, 19, 31, 87,
99, 16, 57, 30, 17, 92, 55, 68, 5, 46, 64, 8, 7, 42, 90, 33, 29, 9, 85, 24,
2, 69, 32, 10, 21, 65, 39, 86, 52, 62, 76, 89, 82, 6, 78, 4, 47, 28, 3, 75,
41, 15, 18, 14, 66, 73, 38, 80, 61, 49, 11, 22, 34, 93, 36, 25, 94, 96, 60, 72},
{6, 90, 16, 43, 20, 55, 94, 8, 62, 59, 47, 32, 81, 67, 42, 35, 71, 19, 54, 49,
2, 88, 38, 5, 53, 77, 85, 11, 73, 34, 3, 72, 95, 58, 82, 64, 44, 29, 93, 14,
56, 98, 4, 37, 45, 75, 86, 60, 87, 7, 31, 17, 89, 97, 10, 70, 36, 99, 46, 63,
76, 41, 48, 84, 9, 52, 1, 24, 83, 25, 68, 18, 50, 26, 30, 27, 61, 0, 91, 33,
51, 40, 79, 39, 15, 74, 28, 78, 69, 96, 57, 65, 21, 80, 22, 23, 12, 13, 92, 66},
},
{
{93, 42, 18, 65, 28, 87, 39, 35, 71, 23, 96, 90, 15, 58, 78, 33, 92, 88, 49, 64,
24, 84, 77, 8, 97, 50, 4, 53, 27, 83, 95, 31, 80, 47, 86, 55, 11, 40, 37, 14,
34, 60, 54, 12, 94, 9, 41, 2, 26, 10, 6, 57, 75, 63, 32, 43, 13, 52, 82, 73,
20, 30, 17, 5, 3, 68, 51, 89, 62, 79, 69, 70, 66, 45, 25, 22, 85, 0, 91, 76,
38, 7, 99, 61, 29, 98, 16, 36, 44, 67, 59, 56, 48, 81, 21, 46, 74, 72, 19, 1},
{78, 91, 97, 77, 75, 85, 32, 2, 40, 86, 38, 18, 56, 47, 50, 87, 27, 92, 64, 39,
98, 23, 33, 72, 84, 58, 16, 99, 19, 65, 55, 73, 74, 42, 4, 48, 95, 8, 51, 3,
37, 82, 96, 69, 14, 44, 12, 21, 76, 70, 36, 15, 71, 41, 20, 31, 54, 13, 0, 9,
94, 67, 1, 60, 81, 61, 11, 6, 90, 5, 49, 10, 34, 7, 35, 30, 25, 88, 79, 93,
66, 62, 22, 24, 52, 17, 89, 45, 83, 57, 28, 46, 63, 68, 26, 29, 80, 59, 43, 53},
{78, 91, 97, 77, 75, 85, 32, 2, 40, 86, 38, 18, 56, 47, 50, 87, 27, 92, 64, 39,
98, 23, 33, 72, 84, 58, 16, 99, 19, 65, 55, 73, 74, 42, 4, 48, 95, 8, 51, 3,
37, 82, 96, 69, 14, 44, 12, 21, 76, 70, 36, 15, 71, 41, 20, 31, 54, 13, 0, 9,
94, 67, 1, 60, 81, 61, 11, 6, 90, 5, 49, 10, 34, 7, 35, 30, 25, 88, 79, 93,
66, 62, 22, 24, 52, 17, 89, 45, 83, 57, 28, 46, 63, 68, 26, 29, 80, 59, 43, 53},
{41, 65, 16, 86, 77, 67, 51, 81, 50, 25, 90, 87, 98, 99, 23, 9, 4, 97, 39, 36,
26, 42, 13, 66, 82, 22, 47, 88, 49, 46, 59, 78, 20, 19, 7, 93, 56, 84, 40, 61,
89, 21, 74, 1, 54, 44, 34, 6, 28, 8, 43, 76, 48, 71, 85, 38, 83, 18, 24, 79,
68, 80, 45, 33, 91, 27, 32, 70, 95, 3, 58, 60, 73, 10, 75, 52, 64, 2, 94, 30,
17, 55, 62, 35, 11, 12, 53, 63, 14, 31, 57, 0, 37, 72, 92, 15, 5, 29, 69, 96},
{74, 50, 44, 80, 11, 46, 39, 54, 24, 3, 7, 62, 41, 8, 75, 23, 57, 82, 51, 86,
97, 79, 22, 60, 42, 14, 28, 67, 32, 73, 48, 56, 89, 26, 25, 77, 21, 68, 10, 2,
58, 76, 92, 95, 38, 29, 43, 35, 12, 16, 55, 17, 63, 91, 45, 64, 59, 13, 47, 31,
83, 5, 99, 49, 78, 71, 96, 53, 36, 90, 1, 66, 15, 0, 85, 65, 27, 4, 98, 94,
6, 84, 72, 30, 70, 20, 61, 69, 37, 93, 34, 87, 9, 52, 88, 81, 33, 19, 40, 18},
{35, 67, 27, 53, 26, 79, 20, 46, 38, 4, 72, 84, 70, 58, 65, 91, 92, 21, 32, 71,
54, 24, 89, 0, 37, 8, 22, 81, 12, 87, 43, 18, 82, 17, 33, 76, 25, 13, 74, 51,
99, 3, 55, 60, 19, 73, 86, 62, 93, 52, 31, 64, 96, 66, 14, 40, 42, 69, 45, 6,
61, 34, 41, 59, 49, 10, 39, 2, 75, 80, 15, 94, 7, 23, 95, 78, 90, 5, 1, 28,
56, 30, 9, 98, 44, 48, 68, 77, 47, 11, 63, 83, 88, 36, 97, 29, 57, 50, 16, 85},
{8, 51, 32, 96, 34, 24, 88, 54, 87, 83, 45, 99, 49, 30, 55, 61, 95, 60, 59, 76,
57, 5, 19, 56, 89, 66, 27, 94, 14, 6, 21, 58, 43, 74, 10, 2, 17, 62, 47, 16,
69, 84, 63, 39, 4, 92, 35, 48, 53, 64, 71, 50, 72, 28, 42, 46, 91, 23, 15, 40,
11, 78, 29, 86, 13, 90, 85, 20, 65, 93, 73, 80, 41, 37, 82, 79, 52, 9, 97, 38,
75, 25, 44, 7, 26, 12, 22, 0, 33, 1, 31, 68, 70, 67, 98, 18, 77, 36, 3, 81},
{34, 17, 91, 45, 63, 64, 1, 71, 98, 78, 94, 3, 99, 12, 15, 93, 69, 19, 61, 80,
92, 81, 33, 51, 40, 10, 75, 24, 5, 87, 73, 46, 52, 86, 4, 70, 89, 56, 2, 67,
72, 58, 49, 84, 29, 18, 22, 38, 95, 36, 43, 35, 37, 85, 57, 8, 76, 7, 27, 28,
55, 74, 42, 13, 77, 31, 60, 39, 48, 68, 97, 79, 44, 54, 88, 82, 21, 66, 9, 23,
59, 50, 47, 16, 83, 25, 6, 30, 41, 96, 90, 14, 62, 20, 65, 11, 26, 32, 53, 0},
{7, 51, 77, 80, 0, 53, 56, 27, 2, 78, 28, 32, 26, 22, 73, 93, 36, 54, 64, 99,
60, 50, 21, 11, 39, 42, 81, 8, 1, 79, 61, 5, 15, 31, 59, 33, 20, 94, 23, 88,
69, 17, 35, 95, 40, 75, 85, 87, 43, 47, 89, 12, 71, 72, 9, 83, 67, 38, 90, 82,
29, 10, 37, 91, 97, 74, 6, 86, 30, 63, 24, 49, 3, 13, 4, 57, 76, 70, 98, 34,
58, 52, 96, 84, 25, 55, 44, 46, 18, 65, 62, 68, 48, 66, 41, 19, 14, 45, 16, 92},
{96, 2, 76, 15, 31, 22, 71, 38, 88, 26, 67, 0, 21, 10, 58, 39, 63, 77, 23, 50,
48, 19, 78, 30, 93, 91, 14, 12, 28, 11, 53, 73, 60, 61, 35, 79, 57, 70, 40, 20,
56, 32, 65, 64, 68, 55, 82, 94, 45, 95, 66, 7, 43, 16, 37, 99, 72, 62, 13, 4,
47, 8, 98, 89, 46, 29, 51, 85, 74, 17, 97, 84, 33, 18, 25, 83, 69, 44, 87, 49,
92, 9, 90, 36, 41, 81, 59, 54, 34, 6, 42, 5, 80, 3, 1, 86, 27, 52, 24, 75},
},
{
{61, 85, 41, 37, 12, 25, 27, 17, 92, 52, 44, 66, 13, 15, 19, 78, 77, 9, 40, 20,
58, 43, 76, 24, 59, 42, 28, 30, 96, 86, 62, 89, 67, 35, 45, 6, 36, 50, 74, 26,
84, 95, 7, 68, 97, 79, 51, 16, 80, 3, 82, 2, 93, 21, 31, 98, 18, 60, 39, 72,
54, 65, 73, 32, 5, 70, 99, 10, 14, 71, 34, 4, 64, 33, 55, 48, 75, 87, 57, 81,
22, 46, 69, 63, 90, 23, 38, 56, 8, 29, 83, 88, 11, 0, 47, 53, 91, 94, 49, 1},
{35, 22, 67, 24, 94, 82, 12, 68, 30, 43, 58, 96, 27, 69, 65, 18, 7, 79, 14, 2,
44, 60, 54, 47, 51, 83, 89, 3, 20, 11, 40, 85, 37, 48, 49, 8, 73, 15, 90, 93,
70, 84, 34, 4, 86, 98, 17, 46, 72, 63, 26, 56, 97, 74, 91, 99, 36, 80, 81, 41,
53, 88, 77, 21, 0, 23, 64, 76, 95, 33, 62, 1, 75, 87, 10, 66, 57, 6, 28, 25,
42, 9, 50, 39, 29, 78, 13, 19, 31, 45, 55, 61, 38, 71, 59, 5, 92, 52, 32, 16},
{70, 29, 50, 57, 31, 9, 59, 45, 87, 71, 98, 89, 77, 97, 86, 5, 21, 61, 47, 41,
76, 37, 65, 12, 58, 75, 78, 91, 27, 63, 64, 83, 55, 95, 4, 11, 82, 23, 39, 40,
42, 68, 69, 48, 54, 13, 99, 33, 26, 0, 66, 79, 46, 72, 49, 44, 25, 74, 73, 28,
38, 10, 2, 85, 15, 19, 52, 94, 7, 36, 17, 32, 96, 3, 20, 30, 92, 51, 14, 90,
24, 67, 43, 34, 93, 84, 62, 81, 1, 56, 53, 88, 80, 35, 22, 6, 18, 16, 8, 60},
{62, 75, 38, 50, 52, 86, 53, 73, 99, 59, 26, 46, 48, 90, 64, 1, 65, 13, 30, 36,
57, 16, 97, 91, 51, 33, 80, 67, 5, 25, 8, 96, 94, 84, 6, 89, 88, 20, 10, 85,
41, 56, 9, 34, 63, 0, 18, 12, 42, 15, 87, 4, 61, 43, 82, 17, 98, 60, 92, 70,
49, 71, 47, 54, 79, 83, 27, 74, 58, 2, 76, 93, 22, 69, 78, 44, 28, 95, 72, 68,
7, 55, 29, 77, 14, 23, 31, 32, 3, 37, 11, 66, 35, 39, 24, 81, 19, 21, 40, 45},
{65, 78, 7, 83, 80, 77, 71, 72, 22, 85, 63, 73, 44, 87, 86, 88, 41, 30, 95, 67,
58, 21, 4, 5, 26, 27, 75, 31, 99, 52, 92, 64, 28, 8, 19, 98, 84, 34, 9, 48,
60, 0, 56, 1, 62, 16, 91, 32, 89, 79, 40, 11, 54, 51, 43, 12, 45, 66, 23, 13,
96, 81, 47, 36, 46, 93, 2, 69, 33, 3, 20, 6, 35, 18, 74, 68, 53, 37, 29, 61,
90, 57, 14, 42, 24, 76, 10, 38, 97, 39, 25, 50, 49, 70, 55, 82, 15, 17, 59, 94},
{11, 17, 21, 48, 63, 0, 73, 14, 10, 88, 2, 28, 6, 9, 19, 69, 81, 13, 35, 95,
60, 56, 30, 47, 91, 92, 86, 29, 76, 50, 16, 25, 82, 93, 8, 5, 58, 94, 98, 20,
74, 77, 4, 52, 97, 79, 27, 42, 36, 12, 26, 72, 71, 45, 24, 87, 1, 39, 22, 89,
53, 31, 61, 37, 33, 18, 59, 43, 80, 15, 54, 38, 78, 90, 32, 83, 23, 44, 55, 66,
64, 3, 67, 49, 84, 62, 51, 41, 46, 85, 96, 34, 99, 75, 7, 40, 70, 68, 65, 57},
{43, 38, 48, 47, 74, 88, 64, 54, 59, 35, 29, 18, 53, 91, 68, 66, 12, 79, 55, 41,
34, 36, 33, 94, 56, 87, 25, 6, 1, 8, 63, 96, 93, 44, 40, 65, 13, 77, 61, 85,
99, 80, 11, 51, 14, 46, 5, 39, 92, 27, 75, 52, 72, 83, 19, 62, 32, 24, 82, 95,
17, 37, 0, 15, 97, 4, 42, 86, 84, 3, 26, 81, 98, 9, 73, 22, 28, 78, 60, 76,
16, 23, 71, 89, 31, 57, 58, 20, 50, 10, 49, 45, 21, 67, 90, 30, 7, 2, 70, 69},
{78, 35, 94, 59, 8, 23, 10, 76, 96, 80, 93, 9, 27, 95, 61, 84, 88, 91, 2, 33,
1, 85, 3, 36, 79, 18, 92, 5, 34, 81, 98, 40, 38, 68, 90, 70, 89, 65, 48, 6,
77, 30, 72, 62, 16, 83, 32, 54, 99, 82, 75, 41, 53, 17, 67, 43, 28, 21, 46, 7,
25, 64, 13, 14, 87, 58, 63, 31, 39, 47, 24, 71, 74, 55, 20, 11, 22, 45, 44, 51,
37, 15, 42, 56, 66, 29, 86, 49, 69, 57, 73, 50, 60, 0, 4, 19, 52, 97, 12, 26},
{88, 86, 41, 1, 70, 10, 19, 2, 11, 44, 87, 26, 53, 59, 40, 18, 61, 13, 21, 56,
85, 75, 25, 5, 97, 7, 36, 67, 76, 79, 90, 65, 91, 0, 98, 89, 20, 55, 93, 58,
28, 63, 6, 83, 47, 71, 73, 49, 39, 31, 48, 69, 82, 50, 78, 62, 14, 74, 29, 54,
57, 80, 32, 94, 3, 66, 30, 35, 8, 46, 16, 51, 24, 17, 4, 84, 45, 60, 34, 15,
72, 77, 42, 12, 64, 9, 33, 95, 81, 96, 38, 43, 52, 99, 22, 92, 27, 23, 37, 68},
{90, 93, 83, 21, 96, 71, 67, 28, 37, 75, 55, 58, 56, 39, 16, 46, 91, 41, 77, 92,
99, 65, 27, 5, 24, 1, 69, 7, 31, 35, 33, 64, 12, 14, 45, 11, 98, 0, 57, 72,
74, 13, 61, 40, 63, 89, 53, 85, 18, 76, 2, 70, 20, 60, 84, 52, 25, 43, 54, 17,
36, 19, 97, 73, 94, 62, 3, 23, 34, 88, 29, 80, 50, 22, 81, 87, 6, 30, 78, 48,
79, 44, 66, 8, 51, 86, 4, 15, 42, 10, 82, 32, 68, 26, 38, 47, 49, 9, 95, 59},
},
};
static const uint8_t access_code_table_2[10][10] = {
{4, 7, 8, 0, 9, 1, 3, 6, 2, 5},
{5, 0, 4, 1, 3, 8, 9, 7, 6, 2},
{0, 2, 7, 8, 9, 1, 6, 3, 5, 4},
{5, 9, 8, 4, 1, 7, 0, 2, 3, 6},
{7, 3, 0, 1, 8, 9, 6, 5, 2, 4},
{7, 2, 4, 0, 1, 9, 6, 3, 5, 8},
{4, 1, 6, 3, 5, 8, 0, 7, 2, 9},
{6, 8, 4, 1, 5, 3, 7, 2, 9, 0},
{4, 1, 8, 3, 7, 2, 0, 6, 5, 9},
{7, 4, 5, 6, 2, 3, 9, 1, 8, 0}};
static void rotate_right(uint8_t* data, int n_bytes, int n_bits) {
uint8_t prior = data[n_bytes - 1];
for(int i = 0; i < n_bytes; i++) {
uint8_t tmp = data[i];
data[i] = (data[i] >> n_bits) | ((prior & ((1 << n_bits) - 1)) << (8 - n_bits));
prior = tmp;
}
}
static void decrypt_spad_0(const uint8_t* spad, uint8_t* decrypted) {
for(int i = 0; i < 16; i++) {
decrypted[i] = s_box[N_TABLES][spad[i]];
}
int count = (decrypted[15] >> 4) + 7;
int table = decrypted[15] + ITERATION_ADD * count;
for(int iter = 0; iter < count; iter++) {
table -= ITERATION_ADD;
rotate_right(decrypted, 15, 5); // only the first 15 bytes
for(int i = 0; i < 15; i++) {
decrypted[i] = s_box[table % N_TABLES][decrypted[i]];
}
}
}
static uint16_t crc16(uint64_t data, int bits, uint16_t init, uint16_t poly) {
uint16_t v = init;
for(int i = 0; i < bits; ++i) {
v = (v >> 1) ^ (((v ^ data) & 1ULL) ? poly : 0);
data >>= 1;
}
return v;
}
static bool
check_access_code_crc(const uint8_t ac[3], const uint8_t body[6], const uint8_t crc[5]) {
uint64_t msg = 0;
for(int i = 0; i < 3; ++i) {
if(ac[i] > 0xF) return false;
msg = (msg << 4) | ac[i];
}
for(int i = 0; i < 6; ++i) {
uint8_t v = body[i];
if(v > 99) return false;
msg = (msg << 4) | (v / 10);
msg = (msg << 4) | (v % 10);
}
uint16_t calculated_crc = crc16(msg, 60, 0xFFFF, 0x8408);
uint16_t expected_crc = crc[0] * 10000 + crc[1] * 1000 + crc[2] * 100 + crc[3] * 10 + crc[4];
return (calculated_crc == expected_crc);
}
static void parse_access_code(const uint8_t* access_code, FuriString* parsed_data) {
furi_assert(access_code);
furi_assert(parsed_data);
uint8_t decrypted[6];
// decrypted contains the decoded serial bytes (as 6 BCD bytes)
for(int i = 0, j = 3; i < 6; ++i, j += 2) {
decrypted[i] = (access_code[j]) * 10 + (access_code[j + 1]);
}
uint8_t crc[5] = {0};
memcpy(crc, access_code + 15, 5);
int boxes1[6] = {
(crc[3] + crc[2]) % 10, crc[2], crc[3], crc[4], (crc[4] + crc[0]) % 10, crc[1]};
for(int n = 0; n < 6; ++n) {
decrypted[n] = access_code_table_1[4][boxes1[n]][decrypted[n]];
}
int rv = decrypted[0] / 10;
int boxes2[6] = {
(crc[1] + crc[0]) % 10, crc[1], crc[2], crc[3], crc[4], (crc[4] + crc[1]) % 10};
for(int n = 0; n < 6; ++n) {
if(n == 0) {
decrypted[n] = (decrypted[n] & 0xF0) |
access_code_table_2[boxes2[n]][decrypted[n] & 0x0F];
} else {
decrypted[n] = access_code_table_1[rv][boxes2[n]][decrypted[n]];
}
}
furi_string_cat_printf(
parsed_data,
"Decrypted serial number:\n%02d%02d%02d%02d%02d%02d\n",
decrypted[0],
decrypted[1],
decrypted[2],
decrypted[3],
decrypted[4],
decrypted[5]);
furi_string_cat_printf(
parsed_data,
"CRC check: %s\n",
check_access_code_crc(access_code, decrypted, crc) ? "Passed" : "Invalid");
}
bool aic_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
furi_assert(parsed_data);
bool parsed = false;
if(nfc_device_get_protocol(device) != NfcProtocolFelica) return false;
const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica);
const uint8_t ic_type = data->pmm.data[1];
if(ic_type != 0xF0 && ic_type != 0xF1) {
// Must be Felica Lite (0xF0) or Lite-S (0xF1) to parse
return false;
}
const uint8_t data_format_code_1 = data->data.fs.id.data[8];
if(data_format_code_1 != 0) {
// We only know Data Format Code {0x00, 0xXX}
return false;
}
parsed = true;
furi_string_printf(parsed_data, "\e#Amusement IC Card\n");
furi_string_cat_str(
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
furi_string_cat_str(parsed_data, "\nType:\n");
// Determine card brand and type
const uint8_t data_format_code_2 = data->data.fs.id.data[9];
switch(data_format_code_2) {
case 0x2A:
furi_string_cat_str(parsed_data, "Bandai Namco Passport\n");
break;
case 0x3A:
furi_string_cat_str(parsed_data, "Bandai Namco Passport (Old)\n");
break;
case 0x68:
furi_string_cat_str(parsed_data, "Konami e-amusement pass\n");
break;
case 0x78:
furi_string_cat_str(parsed_data, "SEGA Aime\n");
break;
case 0x79:
furi_string_cat_str(parsed_data, "Taito NESiCA\n");
break;
default:
parsed = false;
return parsed; // Unknown vendor
}
furi_string_cat_str(
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
// decrypt_spad_0 S-PAD 0
uint8_t decrypted[16] = {0};
decrypt_spad_0(data->data.fs.spad[0].data, decrypted);
// Get Access Code
uint8_t access_code[20] = {0};
for(int i = 0; i < 10; i++) {
access_code[i * 2] = (decrypted[i + 6] & 0xF0) >> 4; // Get upper nibble
access_code[i * 2 + 1] = decrypted[i + 6] & 0x0F; // Get lower nibble
}
furi_string_cat_str(parsed_data, "\nAccess Code:\n");
bool access_code_is_bcd = true;
for(int i = 0; i < 20; i++) {
furi_string_cat_printf(parsed_data, "%d", access_code[i]);
if(i % 4 == 3) {
furi_string_cat_str(parsed_data, " ");
}
if(access_code[i] > 9) access_code_is_bcd = false;
}
furi_string_cat_str(parsed_data, "\n");
furi_string_cat_printf(parsed_data, "BCD valid: %s\n", access_code_is_bcd ? "Yes" : "No");
furi_string_cat_str(
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
// Parse Access Code
if(access_code_is_bcd && access_code[0] == 5) {
furi_string_cat_str(parsed_data, "\n");
parse_access_code(access_code, parsed_data);
} else {
furi_string_cat_printf(
parsed_data, "\nAccess code preamble wrong: expected 5, got %d\n", access_code[0]);
}
furi_string_cat_str(
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
furi_string_cat_str(parsed_data, "\nDecrypted S-PAD 0:\n");
for(int i = 0; i < 16; i++) {
furi_string_cat_printf(parsed_data, "%02X ", decrypted[i]);
if(i == 7) {
furi_string_cat_str(parsed_data, "\n");
}
}
furi_string_cat_str(parsed_data, "\n");
furi_string_cat_str(
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
return parsed;
}
/* Actual implementation of app<>plugin interface */
static const NfcSupportedCardsPlugin aic_plugin = {
.protocol = NfcProtocolFelica,
.verify = NULL,
.read = NULL,
.parse = aic_parse,
};
/* Plugin descriptor to comply with basic plugin specification */
static const FlipperAppPluginDescriptor aic_plugin_descriptor = {
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
.entry_point = &aic_plugin,
};
/* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* aic_plugin_ep(void) {
return &aic_plugin_descriptor;
}

View File

@@ -0,0 +1,284 @@
#include "nfc_supported_card_plugin.h"
#include <flipper_application/flipper_application.h>
#include <nfc/nfc_device.h>
#include <bit_lib/bit_lib.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#define TAG "Banapass"
static const uint64_t banapass_key_b_value_block = 0x019761AA8082;
static const uint64_t banapass_key_b_access_code = 0x574343467632;
typedef struct {
uint64_t a;
uint64_t b;
} MfClassicKeyPair;
static MfClassicKeyPair banapass_keys_if_value_block[] = {
{.a = 0x6090D00632F5, .b = 0x019761AA8082},
{.a = 0xA99164400748, .b = 0x62742819AD7C},
{.a = 0xCC5075E42BA1, .b = 0xB9DF35A0814C},
{.a = 0x8AF9C718F23D, .b = 0x58CD5C3673CB},
{.a = 0xFC80E88EB88C, .b = 0x7A3CDAD7C023},
{.a = 0x30424C029001, .b = 0x024E4E44001F},
{.a = 0xECBBFA57C6AD, .b = 0x4757698143BD},
{.a = 0x1D30972E6485, .b = 0xF8526D1A8D6D},
{.a = 0x1300EC8C7E80, .b = 0xF80A65A87FFA},
{.a = 0xDEB06ED4AF8E, .b = 0x4AD96BF28190},
{.a = 0x000390014D41, .b = 0x0800F9917CB0},
{.a = 0x730050555253, .b = 0x4146D4A956C4},
{.a = 0x131157FBB126, .b = 0xE69DD9015A43},
{.a = 0x337237F254D5, .b = 0x9A8389F32FBF},
{.a = 0x7B8FB4A7100B, .b = 0xC8382A233993},
{.a = 0x7B304F2A12A6, .b = 0xFC9418BF788B},
};
static MfClassicKeyPair banapass_keys_if_access_code[] = {
{.a = 0x6090D00632F5, .b = 0x574343467632},
{.a = 0xA99164400748, .b = 0x62742819AD7C},
{.a = 0xCC5075E42BA1, .b = 0xB9DF35A0814C},
{.a = 0x8AF9C718F23D, .b = 0x58CD5C3673CB},
{.a = 0xFC80E88EB88C, .b = 0x7A3CDAD7C023},
{.a = 0x30424C029001, .b = 0x024E4E44001F},
{.a = 0xECBBFA57C6AD, .b = 0x4757698143BD},
{.a = 0x1D30972E6485, .b = 0xF8526D1A8D6D},
{.a = 0x1300EC8C7E80, .b = 0xF80A65A87FFA},
{.a = 0xDEB06ED4AF8E, .b = 0x4AD96BF28190},
{.a = 0x000390014D41, .b = 0x0800F9917CB0},
{.a = 0x730050555253, .b = 0x4146D4A956C4},
{.a = 0x131157FBB126, .b = 0xE69DD9015A43},
{.a = 0x337237F254D5, .b = 0x9A8389F32FBF},
{.a = 0x7B8FB4A7100B, .b = 0xC8382A233993},
{.a = 0x7B304F2A12A6, .b = 0xFC9418BF788B},
};
static bool banapass_verify(Nfc* nfc) {
bool verified = true;
const uint8_t verify_sector = 0;
uint8_t block_num = mf_classic_get_first_block_num_of_sector(verify_sector);
FURI_LOG_D(TAG, "Verifying sector %u", verify_sector);
MfClassicKey key_a_0 = {};
bit_lib_num_to_bytes_be(
banapass_keys_if_value_block[0].a, COUNT_OF(key_a_0.data), key_a_0.data);
MfClassicAuthContext auth_ctx = {};
MfClassicError error =
mf_classic_poller_sync_auth(nfc, block_num, &key_a_0, MfClassicKeyTypeA, &auth_ctx);
if(error != MfClassicErrorNone) {
FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error);
verified = false;
}
return verified;
}
static bool banapass_read(Nfc* nfc, NfcDevice* device) {
furi_assert(nfc);
furi_assert(device);
bool is_read = false;
MfClassicData* data = mf_classic_alloc();
nfc_device_copy_data(device, NfcProtocolMfClassic, data);
do {
MfClassicType type = MfClassicType1k;
MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);
if(error != MfClassicErrorNone) break;
if(type != MfClassicType1k) {
FURI_LOG_E(TAG, "Card not MIFARE Classic 1k");
break;
}
data->type = type;
MfClassicDeviceKeys keys = {};
// Access Code Read Attempt
for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {
bit_lib_num_to_bytes_be(
banapass_keys_if_access_code[i].a, sizeof(MfClassicKey), keys.key_a[i].data);
FURI_BIT_SET(keys.key_a_mask, i);
bit_lib_num_to_bytes_be(
banapass_keys_if_access_code[i].b, sizeof(MfClassicKey), keys.key_b[i].data);
FURI_BIT_SET(keys.key_b_mask, i);
}
MfClassicError error_access_code = mf_classic_poller_sync_read(nfc, &keys, data);
if(error_access_code == MfClassicErrorNone) {
nfc_device_set_data(device, NfcProtocolMfClassic, data);
is_read = true;
break;
}
// Value Block Read Attempt
for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {
bit_lib_num_to_bytes_be(
banapass_keys_if_value_block[i].a, sizeof(MfClassicKey), keys.key_a[i].data);
FURI_BIT_SET(keys.key_a_mask, i);
bit_lib_num_to_bytes_be(
banapass_keys_if_value_block[i].b, sizeof(MfClassicKey), keys.key_b[i].data);
FURI_BIT_SET(keys.key_b_mask, i);
}
MfClassicError error_value_block = mf_classic_poller_sync_read(nfc, &keys, data);
if(error_value_block == MfClassicErrorNone) {
nfc_device_set_data(device, NfcProtocolMfClassic, data);
is_read = true;
break;
}
FURI_LOG_E(TAG, "Failed to read data. Bad keys?");
} while(false);
mf_classic_free(data);
return is_read;
}
static bool banapass_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
bool parsed = false;
do {
// verify key
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 0);
uint64_t key_a = bit_lib_bytes_to_num_be(sec_tr->key_a.data, 6);
uint64_t key_b = bit_lib_bytes_to_num_be(sec_tr->key_b.data, 6);
if(key_a != banapass_keys_if_value_block[0].a) break;
furi_string_set_str(parsed_data, "\e#Banapass\n");
furi_string_cat_str(
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
furi_string_cat_str(parsed_data, "\nBandai Namco Passport\n");
// banapass Magic is stored at block 1, byte 2-7
uint8_t magic_bytes[6];
for(int i = 0; i < 6; i++) {
magic_bytes[i] = data->block[1].data[2 + i];
}
// verify banapass magic
if(magic_bytes[0] != 'N' || magic_bytes[1] != 'B' || magic_bytes[2] != 'G' ||
magic_bytes[3] != 'I' || magic_bytes[4] != 'C')
break;
// banapass checksum is stored at block 1, starts from byte 8-15
uint8_t check_sum[8];
for(int i = 0; i < 8; i++) {
check_sum[i] = data->block[1].data[8 + i];
}
furi_string_cat_str(
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
bool is_block_2_null = true;
for(int i = 0; i < 16; i++) {
if(data->block[2].data[i] != 0) {
is_block_2_null = false;
break;
}
}
if(is_block_2_null) {
furi_string_cat_str(
parsed_data,
"\nPlease scan the clone at the\nnearest CHUNITHM or\nmaimai Cabinet for the\nAccess Code.\n");
furi_string_cat_str(
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
} else {
switch(key_b) {
case banapass_key_b_value_block:
int32_t value = 0;
uint8_t addr = 0;
bool value_found = mf_classic_block_to_value(
&data->block[2], &value, &addr); // block 2 is value block
if(value_found) {
furi_string_cat_printf(parsed_data, "\nValue: %08lX", value);
} else {
furi_string_cat_str(parsed_data, "\nPotential clone:\nInvalid value block.");
}
furi_string_cat_str(
parsed_data,
"\nPlease check the back of\nyour Bandai Namco Passport\nfor the Access Code.\n");
furi_string_cat_str(
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
break;
case banapass_key_b_access_code:
// banapass access code is stored as decimal hex representation in block 2, starts from byte 6, len 10 bytes
uint8_t access_code[10];
furi_string_cat_printf(parsed_data, "\nAccess Code:\n");
bool access_code_is_bcd = true;
for(int i = 0; i < 10; i++) {
access_code[i] = data->block[2].data[6 + i];
furi_string_cat_printf(parsed_data, "%02X", access_code[i]);
if(i % 2 == 1) {
furi_string_cat_str(parsed_data, " ");
}
if((access_code[i] >> 4) > 9) access_code_is_bcd = false;
if((access_code[i] & 0x0F) > 9) access_code_is_bcd = false;
}
furi_string_cat_printf(
parsed_data, "\nBCD valid: %s\n", access_code_is_bcd ? "Yes" : "No");
if((access_code[0] >> 4) != 3) {
furi_string_cat_printf(
parsed_data,
"Potential clone:\nAccess Code preamble\nexpected 3, got %d\n",
(access_code[0] >> 4));
}
furi_string_cat_str(
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
break;
default:
break;
}
}
furi_string_cat_str(parsed_data, "\nMagic:\n");
for(int i = 0; i < 6; i++) {
furi_string_cat_printf(parsed_data, "%c", magic_bytes[i]);
}
furi_string_cat_str(parsed_data, "\nChecksum:\n");
for(int i = 0; i < 8; i++) {
furi_string_cat_printf(parsed_data, "%02X ", check_sum[i]);
}
furi_string_cat_str(parsed_data, "\n");
furi_string_cat_str(
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
parsed = true;
} while(false);
return parsed;
}
/* Actual implementation of app<>plugin interface */
static const NfcSupportedCardsPlugin banapass_plugin = {
.protocol = NfcProtocolMfClassic,
.verify = banapass_verify,
.read = banapass_read,
.parse = banapass_parse,
};
/* Plugin descriptor to comply with basic plugin specification */
static const FlipperAppPluginDescriptor banapass_plugin_descriptor = {
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
.entry_point = &banapass_plugin,
};
/* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* banapass_plugin_ep(void) {
return &banapass_plugin_descriptor;
}

View File

@@ -0,0 +1,115 @@
Filetype: NFC Vendors
Version: 1
# Please do not change IDs in this list. Add new to the end if necessary.
# ID: "Vendor Country"
1: Motorola UK
2: ST Microelectronics SA France
3: Hitachi, Ltd Japan
4: NXP Semiconductors Germany
5: Infineon Technologies AG Germany
6: Cylink USA
7: Texas Instrument France
8: Fujitsu Limited Japan
9: Matsushita Electronics Corporation, Semiconductor Company Japan
10: NEC Japan
11: Oki Electric Industry Co. Ltd Japan
12: Toshiba Corp. Japan
13: Mitsubishi Electric Corp. Japan
14: Samsung Electronics Co. Ltd Korea
15: Hynix / Hyundai, Korea
16: LG-Semiconductors Co. Ltd Korea
17: Emosyn-EM Microelectronics USA
18: INSIDE Technology France
19: ORGA Kartensysteme GmbH Germany
20: SHARP Corporation Japan
21: ATMEL France
22: EM Microelectronic-Marin SA Switzerland
23: KSW Microtec GmbH Germany
24: ZMD AG Germany
25: XICOR, Inc. USA
26: Sony Corporation Japan
27: Malaysia Microelectronic Solutions Sdn. Bhd Malaysia
28: Emosyn USA
29: Shanghai Fudan Microelectronics Co. Ltd. P.R. China
30: Magellan Technology Pty Limited Australia
31: Melexis NV BO Switzerland
32: Renesas Technology Corp. Japan
33: TAGSYS France
34: Transcore USA
35: Shanghai belling corp., ltd. China
36: Masktech Germany Gmbh Germany
37: Innovision Research and Technology Plc UK
38: Hitachi ULSI Systems Co., Ltd. Japan
39: Cypak AB Sweden
40: Ricoh Japan
41: ASK France
42: Unicore Microsystems, LLC Russian Federation
43: Dallas Semiconductor/Maxim USA
44: Impinj, Inc. USA
45: RightPlug Alliance USA
46: Broadcom Corporation USA
47: MStar Semiconductor, Inc Taiwan, ROC
48: BeeDar Technology Inc. USA
49: RFIDsec Denmark
50: Schweizer Electronic AG Germany
51: AMIC Technology Corp Taiwan
52: Mikron JSC Russia
53: Fraunhofer Institute for Photonic Microsystems Germany
54: IDS Microchip AG Switzerland
55: Thinfilm - Kovio USA
56: HMT Microelectronic Ltd Switzerland
57: Silicon Craft Technology Thailand
58: Advanced Film Device Inc. Japan
59: Nitecrest Ltd UK
60: Verayo Inc. USA
61: HID Global USA
62: Productivity Engineering Gmbh Germany
63: Austriamicrosystems AG (reserved) Austria
64: Gemalto SA France
65: Renesas Electronics Corporation Japan
66: 3Alogics Inc Korea
67: Top TroniQ Asia Limited Hong Kong
68: Gentag Inc. USA
69: Invengo Information Technology Co.Ltd China
70: Guangzhou Sysur Microelectronics, Inc China
71: CEITEC S.A. Brazil
72: Shanghai Quanray Electronics Co. Ltd. China
73: MediaTek Inc Taiwan
74: Angstrem PJSC Russia
75: Celisic Semiconductor (Hong Kong) Limited China
76: LEGIC Identsystems AG Switzerland
77: Balluff GmbH Germany
78: Oberthur Technologies France
79: Silterra Malaysia Sdn. Bhd. Malaysia
80: DELTA Danish Electronics, Light & Acoustics Denmark
81: Giesecke & Devrient GmbH Germany
82: Shenzhen China Vision Microelectronics Co., Ltd. China
83: Shanghai Feiju Microelectronics Co. Ltd. China
84: Intel Corporation USA
85: Microsensys GmbH Germany
86: Sonix Technology Co., Ltd. Taiwan
87: Qualcomm Technologies Inc USA
88: Realtek Semiconductor Corp Taiwan
89: Freevision Technologies Co. Ltd China
90: Giantec Semiconductor Inc. China
91: JSC Angstrem-T Russia
92: STARCHIP France
93: SPIRTECH France
94: GANTNER Electronic GmbH Austria
95: Nordic Semiconductor Norway
96: Verisiti Inc USA
97: Wearlinks Technology Inc. China
98: Userstar Information Systems Co., Ltd Taiwan
99: Pragmatic Printing Ltd. UK
100: Associacao do Laboratorio de Sistemas Integraveis Tecnologico - LSI-TEC Brazil
101: Tendyron Corporation China
102: MUTO Smart Co., Ltd. Korea
103: ON Semiconductor USA
104: TUBITAK BILGEM Turkey
105: Huada Semiconductor Co., Ltd China
106: SEVENEY France
107: ISSM France
108: Wisesec Ltd Israel
124: DB HiTek Co Ltd Korea
125: SATO Vicinity Australia
126: Holtek Taiwan

View File

@@ -69,4 +69,6 @@ ADD_SCENE(nfc, slix_key_input, SlixKeyInput)
ADD_SCENE(nfc, slix_unlock, SlixUnlock)
ADD_SCENE(nfc, slix_unlock_success, SlixUnlockSuccess)
ADD_SCENE(nfc, felica_more_info, FelicaMoreInfo)
ADD_SCENE(nfc, generate_info, GenerateInfo)

View File

@@ -0,0 +1,151 @@
#include "../nfc_app_i.h"
#include "../helpers/protocol_support/nfc_protocol_support_gui_common.h"
#include "../helpers/protocol_support/felica/felica_render.h"
enum {
FelicaMoreInfoStateMenu,
FelicaMoreInfoStateItem, // MUST be last, states >= this correspond with submenu index
};
enum SubmenuIndex {
SubmenuIndexDirectory,
SubmenuIndexDynamic, // dynamic indices start here
};
void nfc_scene_felica_more_info_on_enter(void* context) {
NfcApp* nfc = context;
Submenu* submenu = nfc->submenu;
const uint32_t state =
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneFelicaMoreInfo);
const FelicaData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolFelica);
submenu_add_item(
submenu,
"Directory",
SubmenuIndexDirectory,
nfc_protocol_support_common_submenu_callback,
nfc);
FuriString* label = furi_string_alloc();
switch(data->workflow_type) {
case FelicaLite:
furi_string_printf(label, "All blocks");
submenu_add_item(
submenu,
furi_string_get_cstr(label),
SubmenuIndexDynamic,
nfc_protocol_support_common_submenu_callback,
nfc);
break;
case FelicaStandard:
for(uint32_t i = 0; i < simple_array_get_count(data->services); ++i) {
const FelicaService* service = simple_array_cget(data->services, i);
bool is_public = (service->attr & FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ) == 1;
if(!is_public) {
continue;
}
furi_string_printf(label, "Readable serv %04X", service->code);
submenu_add_item(
submenu,
furi_string_get_cstr(label),
i + SubmenuIndexDynamic,
nfc_protocol_support_common_submenu_callback,
nfc);
}
break;
default:
break;
}
furi_string_free(label);
if(state >= FelicaMoreInfoStateItem) {
submenu_set_selected_item(
nfc->submenu, state - FelicaMoreInfoStateItem + SubmenuIndexDynamic);
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneFelicaMoreInfo, FelicaMoreInfoStateMenu);
}
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
}
bool nfc_scene_felica_more_info_on_event(void* context, SceneManagerEvent event) {
NfcApp* nfc = context;
bool consumed = false;
const uint32_t state =
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneFelicaMoreInfo);
const FelicaData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolFelica);
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexDirectory) {
FuriString* temp_str = furi_string_alloc();
nfc_more_info_render_felica_dir(data, temp_str);
widget_add_text_scroll_element(
nfc->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));
furi_string_free(temp_str);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
scene_manager_set_scene_state(
nfc->scene_manager,
NfcSceneFelicaMoreInfo,
FelicaMoreInfoStateItem + SubmenuIndexDirectory);
consumed = true;
} else {
const uint16_t service_ind = event.event - 1; // offset the three enums above
text_box_reset(nfc->text_box);
furi_string_reset(nfc->text_box_store);
switch(data->workflow_type) {
case FelicaLite:
nfc_more_info_render_felica_lite_dump(data, nfc->text_box_store);
break;
case FelicaStandard:
const FelicaService* service = simple_array_cget(data->services, service_ind);
furi_string_cat_printf(nfc->text_box_store, "Service 0x%04X\n", service->code);
nfc_more_info_render_felica_blocks(data, nfc->text_box_store, service->code);
break;
default:
furi_string_set_str(nfc->text_box_store, "IC type not implemented yet");
break;
}
text_box_set_font(nfc->text_box, TextBoxFontHex);
text_box_set_text(nfc->text_box, furi_string_get_cstr(nfc->text_box_store));
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneFelicaMoreInfo, FelicaMoreInfoStateItem + event.event);
consumed = true;
}
} else if(event.type == SceneManagerEventTypeBack) {
if(state >= FelicaMoreInfoStateItem) {
widget_reset(nfc->widget);
text_box_reset(nfc->text_box);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneFelicaMoreInfo, FelicaMoreInfoStateMenu);
} else {
widget_reset(nfc->widget);
text_box_reset(nfc->text_box);
// Return directly to the Info scene
scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneInfo);
}
consumed = true;
}
return consumed;
}
void nfc_scene_felica_more_info_on_exit(void* context) {
NfcApp* nfc = context;
// Clear views
widget_reset(nfc->widget);
text_box_reset(nfc->text_box);
furi_string_reset(nfc->text_box_store);
submenu_reset(nfc->submenu);
}

View File

@@ -3,7 +3,7 @@
#define TAG "NfcMfUlCDictAttack"
// TODO: Support card_detected properly
// TODO: Support card_detected properly -nofl
enum {
DictAttackStateUserDictInProgress,

View File

@@ -25,7 +25,7 @@ static void onewire_cli_search(PipeSide* pipe) {
onewire_host_start(onewire);
power_enable_otg(power, true);
while(!done) {
while(!done) { //-V1044
if(onewire_host_search(onewire, address, OneWireHostSearchModeNormal) != 1) {
printf("Search finished\r\n");
onewire_host_reset_search(onewire);

View File

@@ -22,7 +22,7 @@ typedef struct {
float longitude;
} SubGhzHistoryItem;
ARRAY_DEF(SubGhzHistoryItemArray, SubGhzHistoryItem, M_POD_OPLIST)
ARRAY_DEF(SubGhzHistoryItemArray, SubGhzHistoryItem, M_POD_OPLIST) //-V658
#define M_OPL_SubGhzHistoryItemArray_t() ARRAY_OPLIST(SubGhzHistoryItemArray, M_POD_OPLIST)

View File

@@ -23,7 +23,7 @@ typedef struct {
uint16_t repeats;
} SubGhzReceiverMenuItem;
ARRAY_DEF(SubGhzReceiverMenuItemArray, SubGhzReceiverMenuItem, M_POD_OPLIST)
ARRAY_DEF(SubGhzReceiverMenuItemArray, SubGhzReceiverMenuItem, M_POD_OPLIST) //-V658
#define M_OPL_SubGhzReceiverMenuItemArray_t() \
ARRAY_OPLIST(SubGhzReceiverMenuItemArray, M_POD_OPLIST)