mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-04-24 03:29:57 -07:00
Merge branch 'feat/nfc-type-4-final' into mntm-dev
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
#
|
||||
@@ -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
|
||||
#
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
252
applications/main/lfrfid/resources/lfrfid/assets/iso3166.lfrfid
Normal file
252
applications/main/lfrfid/resources/lfrfid/assets/iso3166.lfrfid
Normal 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
|
||||
@@ -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"],
|
||||
)
|
||||
|
||||
311
applications/main/nfc/cli/commands/apdu/nfc_cli_command_apdu.c
Normal file
311
applications/main/nfc/cli/commands/apdu/nfc_cli_command_apdu.c
Normal 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
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../nfc_cli_command_base_i.h"
|
||||
|
||||
extern const NfcCliCommandDescriptor apdu_cmd;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_apdu_common_types.h"
|
||||
|
||||
NfcCommand
|
||||
nfc_cli_apdu_iso14443_4a_handler(NfcGenericEvent event, NfcCliApduRequestResponse* instance);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_apdu_common_types.h"
|
||||
|
||||
NfcCommand
|
||||
nfc_cli_apdu_iso14443_4b_handler(NfcGenericEvent event, NfcCliApduRequestResponse* instance);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_apdu_common_types.h"
|
||||
|
||||
NfcCommand
|
||||
nfc_cli_apdu_iso15693_3_handler(NfcGenericEvent event, NfcCliApduRequestResponse* instance);
|
||||
@@ -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;
|
||||
319
applications/main/nfc/cli/commands/dump/nfc_cli_command_dump.c
Normal file
319
applications/main/nfc/cli/commands/dump/nfc_cli_command_dump.c
Normal 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
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../nfc_cli_command_base_i.h"
|
||||
|
||||
extern const NfcCliCommandDescriptor dump_cmd;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_dump_common_types.h"
|
||||
|
||||
NfcCommand nfc_cli_dump_poller_callback_felica(NfcGenericEvent event, void* context);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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, §or_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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_dump_common_types.h"
|
||||
|
||||
NfcCommand nfc_cli_dump_poller_callback_slix(NfcGenericEvent event, void* context);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_dump_common_types.h"
|
||||
|
||||
NfcCommand nfc_cli_dump_poller_callback_st25tb(NfcGenericEvent event, void* context);
|
||||
61
applications/main/nfc/cli/commands/helpers/nfc_cli_format.c
Normal file
61
applications/main/nfc/cli/commands/helpers/nfc_cli_format.c
Normal 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]);
|
||||
}
|
||||
}
|
||||
16
applications/main/nfc/cli/commands/helpers/nfc_cli_format.h
Normal file
16
applications/main/nfc/cli/commands/helpers/nfc_cli_format.h
Normal 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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
96
applications/main/nfc/cli/commands/helpers/nfc_cli_scanner.c
Normal file
96
applications/main/nfc/cli/commands/helpers/nfc_cli_scanner.c
Normal 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;
|
||||
}
|
||||
21
applications/main/nfc/cli/commands/helpers/nfc_cli_scanner.h
Normal file
21
applications/main/nfc/cli/commands/helpers/nfc_cli_scanner.h
Normal 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);
|
||||
227
applications/main/nfc/cli/commands/mfu/nfc_cli_action_info.c
Normal file
227
applications/main/nfc/cli/commands/mfu/nfc_cli_action_info.c
Normal 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));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
52
applications/main/nfc/cli/commands/mfu/nfc_cli_action_rdbl.c
Normal file
52
applications/main/nfc/cli/commands/mfu/nfc_cli_action_rdbl.c
Normal 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;
|
||||
}
|
||||
@@ -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);
|
||||
71
applications/main/nfc/cli/commands/mfu/nfc_cli_action_wrbl.c
Normal file
71
applications/main/nfc/cli/commands/mfu/nfc_cli_action_wrbl.c
Normal 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;
|
||||
}
|
||||
@@ -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);
|
||||
77
applications/main/nfc/cli/commands/mfu/nfc_cli_command_mfu.c
Normal file
77
applications/main/nfc/cli/commands/mfu/nfc_cli_command_mfu.c
Normal 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);
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../nfc_cli_command_base_i.h"
|
||||
|
||||
extern const NfcCliCommandDescriptor mfu_cmd;
|
||||
126
applications/main/nfc/cli/commands/nfc_cli_command_emulate.c
Normal file
126
applications/main/nfc/cli/commands/nfc_cli_command_emulate.c
Normal 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
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_command_base_i.h"
|
||||
|
||||
extern const NfcCliCommandDescriptor emulate_cmd;
|
||||
28
applications/main/nfc/cli/commands/nfc_cli_command_field.c
Normal file
28
applications/main/nfc/cli/commands/nfc_cli_command_field.c
Normal 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,
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_command_base_i.h"
|
||||
|
||||
extern const NfcCliCommandDescriptor field_cmd;
|
||||
97
applications/main/nfc/cli/commands/nfc_cli_command_scanner.c
Normal file
97
applications/main/nfc/cli/commands/nfc_cli_command_scanner.c
Normal 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);
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_cli_command_base_i.h"
|
||||
|
||||
extern const NfcCliCommandDescriptor scanner_cmd;
|
||||
352
applications/main/nfc/cli/commands/raw/nfc_cli_command_raw.c
Normal file
352
applications/main/nfc/cli/commands/raw/nfc_cli_command_raw.c
Normal 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
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../nfc_cli_command_base_i.h"
|
||||
|
||||
extern const NfcCliCommandDescriptor raw_cmd;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
124
applications/main/nfc/cli/nfc_cli.c
Normal file
124
applications/main/nfc/cli/nfc_cli.c
Normal 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);
|
||||
69
applications/main/nfc/cli/nfc_cli_command_base.h
Normal file
69
applications/main/nfc/cli/nfc_cli_command_base.h
Normal 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;
|
||||
130
applications/main/nfc/cli/nfc_cli_command_base_i.h
Normal file
130
applications/main/nfc/cli/nfc_cli_command_base_i.h
Normal 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); \
|
||||
}
|
||||
388
applications/main/nfc/cli/nfc_cli_command_processor.c
Normal file
388
applications/main/nfc/cli/nfc_cli_command_processor.c
Normal 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);
|
||||
}
|
||||
15
applications/main/nfc/cli/nfc_cli_command_processor.h
Normal file
15
applications/main/nfc/cli/nfc_cli_command_processor.h
Normal 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);
|
||||
161
applications/main/nfc/cli/nfc_cli_commands.c
Normal file
161
applications/main/nfc/cli/nfc_cli_commands.c
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
27
applications/main/nfc/cli/nfc_cli_commands.h
Normal file
27
applications/main/nfc/cli/nfc_cli_commands.h
Normal 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);
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
678
applications/main/nfc/plugins/supported_cards/aic.c
Normal file
678
applications/main/nfc/plugins/supported_cards/aic.c
Normal 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;
|
||||
}
|
||||
284
applications/main/nfc/plugins/supported_cards/banapass.c
Normal file
284
applications/main/nfc/plugins/supported_cards/banapass.c
Normal 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;
|
||||
}
|
||||
115
applications/main/nfc/resources/nfc/assets/vendors.nfc
Normal file
115
applications/main/nfc/resources/nfc/assets/vendors.nfc
Normal 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
|
||||
@@ -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)
|
||||
|
||||
151
applications/main/nfc/scenes/nfc_scene_felica_more_info.c
Normal file
151
applications/main/nfc/scenes/nfc_scene_felica_more_info.c
Normal 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);
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#define TAG "NfcMfUlCDictAttack"
|
||||
|
||||
// TODO: Support card_detected properly
|
||||
// TODO: Support card_detected properly -nofl
|
||||
|
||||
enum {
|
||||
DictAttackStateUserDictInProgress,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user