{"id":20018810,"url":"https://github.com/hamarituc/desfsh","last_synced_at":"2026-01-18T02:04:44.206Z","repository":{"id":134122358,"uuid":"349787614","full_name":"hamarituc/desfsh","owner":"hamarituc","description":"DESFire Shell","archived":false,"fork":false,"pushed_at":"2023-07-26T09:05:45.000Z","size":216,"stargazers_count":11,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-05-04T23:36:18.658Z","etag":null,"topics":["commandline-tool","desfire","nfc"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hamarituc.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-03-20T17:08:39.000Z","updated_at":"2024-11-02T19:03:34.000Z","dependencies_parsed_at":null,"dependency_job_id":"2deb65c9-3f22-4e91-a3e9-5ed9ecd36657","html_url":"https://github.com/hamarituc/desfsh","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/hamarituc/desfsh","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hamarituc%2Fdesfsh","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hamarituc%2Fdesfsh/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hamarituc%2Fdesfsh/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hamarituc%2Fdesfsh/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hamarituc","download_url":"https://codeload.github.com/hamarituc/desfsh/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hamarituc%2Fdesfsh/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267723925,"owners_count":24134238,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-29T02:00:12.549Z","response_time":2574,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["commandline-tool","desfire","nfc"],"created_at":"2024-11-13T08:24:24.268Z","updated_at":"2026-01-18T02:04:44.199Z","avatar_url":"https://github.com/hamarituc.png","language":"C","readme":"DESFire-Shell\n=============\n\nThis tool allows you to modify DESFire NFC tags via a simple command line user\ninterface. The command line language is simply an interactive Lua shell.\nDESFire operations are mapped to Lua function calls.\n\n\nInstallation\n------------\n\n### Prerequirements\n\n#### Build Time\n\n * pkgconfig\n\n#### Run Time\n\n * GNU Readline (https://tiswww.case.edu/php/chet/readline/rltop.html)\n * Lua 5.1 or newer (http://www.lua.org/)\n * OpenSSL 1.1.1 or 3 (https://www.openssl.org/)\n * zlib (https://www.zlib.net/)\n * libnfc (https://github.com/nfc-tools/libnfc)\n * libfreefare (https://github.com/nfc-tools/libfreefare)\n\n\n### Build\n\n```\n$ make\n$ cp desfsh /usr/local/bin\n```\n\n\nUsage\n-----\n\n### Show Devices and Tags\n\nSimply calling `desfsh` list the available card reader devices. In the\nfollowing example all readers are virtually the same, just the drivers to\naccess the reader differ.\n\n```\n3 devices found:\n 0: acr122_usb:001:097\n 1: acr122_pcsc:ACS ACR122U PICC Interface 01 00\n  ** Unable to open device. **\n 2: acr122_pcsc:ACS ACR122U PICC Interface 02 00\n```\n\nWith a NFC tag present, each reader lists detected tags. Here two NFC tags are\npresent in the radio field of the reader.\n\n```\n3 devices found:\n 0: acr122_usb:001:097\n   0: Mifare DESFire --\u003e 044f6bf2893180\n   1: Mifare DESFire --\u003e 04257a020c5180\n 1: acr122_pcsc:ACS ACR122U PICC Interface 01 00\n  ** Unable to open device. **\n 2: acr122_pcsc:ACS ACR122U PICC Interface 02 00\n   0: Mifare DESFire --\u003e 044f6bf2893180\n   1: Mifare DESFire --\u003e 04257a020c5180\n```\n\n### Access a Tag\n\nTo access a distinct tag, you have to provide the reader and tag identifier as\nprovided by the above-mentioned output. You may either specify the index ...\n\n```\n./desfsh -D acr122_usb:001:097 -T 044f6bf2893180\n```\n\n... or the textual identifier ...\n\n```\n./desfsh -d 0 -t 0\n```\n\n... or a combination of both.\n\n```\n./desfsh -d 0 -T 044f6bf2893180\n```\n\nYou will get an interactive Lua shell which lets you execute commands against\nthe tag.\n\n```\n\u003e result, errmsg, aids = cmd.appids()\n\u003e print(result, errmsg)\n0       OK\n\u003e for idx, aid in ipairs(aids) do\n\u003e\u003e print(idx, aid)\n\u003e\u003e end\n1       0x000001\n```\n\n### Command Mode\n\nYou can specifiy a command via the `-c`-option. The program exits after the\ncommand string has been executed.\n\nIn the following example the script `examples/clt21.lua` is executed.\n\n**CAUTION:** This script will delete all applications irremediably on the card.\n  It will try to authenticated with a zero DES or AES key respectively and\n  change the PICC Master Key to the zero AES key. Afterwards a sample\n  application with AID 1 and a couple of sample files is created.\n\n```\n./desfsh -d 0 -t 0 -c 'dofile(\"examples/clt21.lua\")'\n```\n\nUsing the `-i`-option, the program will enter the interactive mode after\nexecuting the provided command string instead of exiting.\n\n### Offline Mode\n\nUsing the `-o`-option enters offline mode. In offline mode no card reader is\nrequired. The features of the program are limited to the functions which don't\ndepend on a connection to cards. This is mainly useful to perform cryptographic\ncalculations.\n\n### Debug Mode\n\nYou can inspect all input and out parameters as well as status codes by setting\nthe appropriate debug flags. The debug level is a logical or combination of\nserveral flags. The following flags are defined.\n\n* `0x01` Show status codes of executed commands. Command names are printed in\n  purple. Successful commands are printed in green, failed commands in red.\n* `0x02` Show input parameters. Input parameters are printed in blue.\n* `0x04` Show returned parameters. Returned parameters are printed in cyan.\n* `0x08` Show additions debug information.\n\n```\n\u003e debugset(15)\n\u003e cmd.appids()\n\u003e\u003e\u003e GetApplicationIDs \u003c\u003c\u003c\n    STAT \u003c=\u003e 0: OK\n     AID \u003c=  0x000001\n\u003e cmd.select(1)\n\u003e\u003e\u003e SelectApplication \u003c\u003c\u003c\n     AID  =\u003e 0x000001\n    STAT \u003c=\u003e 0: OK\n```\n\nIn the example all debug flags are set. Then all defined applications are\nqueried. The command executes successful (`STAT \u003c=\u003e 0: OK`). There is one\napplications ID returned (`AID \u003c=  0x000001`). In case multiple applications\nwould be available on the tag, further `AID`-lines would be printed out. Lastly\nthe application with ID 1 is selected. The AID 1 is transferred to the tag\n(`AID  =\u003e 0x000001`). The command executes successfully, too.\n\n### Help\n\nThe `help`-command shows an online help for all commands. Without an command\nname, all available commands are listed.\n\n```\n\u003e help()\n\nbuf.concat            Concatenate buffers\nbuf.fromascii         Read Buffer from ASCII String\nbuf.fromhexstr        Read Buffer from HEX String\nbuf.fromtable         Read Buffer from Table\nbuf.hexdump           Convert Buffer to HEX Dump\nbuf.toascii           Convert Buffer to ASCII String\nbuf.tohexstr          Convert Buffer to HEX String\nbuf.totable           Convert Buffer to Table\ncmd.abort             Abort Transaction\ncmd.appids            Get Application List\ncmd.auth              Authenticate to PICC\ncmd.carduid           Get Real Card UID\ncmd.cbdf              Create Backup Data File\ncmd.ccrf              Create Cyclic Record File\ncmd.cfs               Change File Settings\ncmd.ck                Change Key\ncmd.cks               Change Master Key Configuration\ncmd.clrf              Create Linear Record File\ncmd.commit            Commit Transaction\ncmd.createapp         Create Application\ncmd.crec              Clear Record File\ncmd.credit            Increase a Files Value\ncmd.csdf              Create Standard Data File\ncmd.cvf               Create Value File\ncmd.debit             Decrease a Files Value\ncmd.deleteapp         Delete Application\ncmd.delf              Delete File\ncmd.fileids           Get File List\ncmd.format            Format PICC\ncmd.freemem           Get Size of remaining Memory\ncmd.getval            Get Value of File\ncmd.getver            Get PICC Information\ncmd.gfs               Get File Settings\ncmd.gks               Get Master Key Configuration\ncmd.gkv               Get Key Version\ncmd.lcredit           Increase a Files Value by a Limited Amount\ncmd.read              Read from File\ncmd.rrec              Read from Record\ncmd.selapp            Select Application\ncmd.wrec              Write to Record\ncmd.write             Write to File\ncrc.crc32             Calculate a CRC-32 checksum\ncrypto.cmac           Calculate CMAC\ncrypto.hmac           Calculate HMAC\ndebugset              Set Debug Flags\nhelp                  Show Help Text\nkey.create            Create key object\nkey.diversify         Calculate diversified Key\nshow.apps             Show Application Information\nshow.files            Show Files of an Application\nshow.picc             Show PICC Information\n```\n\nWhen you specify a command, a brief description is printed out including the\ninput and output parameters of the command. Commands may have aliases. For\nexample `cmd.appids`, `cmd.aids` and `cmd.GetApplicationIDs` are all the same\ncommand.\n\n```\n\u003e help(cmd.appids)\n\nGet Application List\n\ncode, err, [aids] = cmd.appids()\n\nAliasses: appids, aids, GetApplicationIDs\n\n    code        Return Code\n    err         Error String\n    aids        List of AIDs\n\n\u003e help(cmd.select)\n\nSelect Application\n\ncode, err = cmd.selapp(aid)\n\nAliasses: selapp, select, SelectApplication\n\n    aid         AID\n    code        Return Code\n    err         Error String\n```\n\n\nCommands\n--------\n\nCommands are grouped into namespaces.\n\n| Namespace | Description                                         |\n| --------- | --------------------------------------------------- |\n| `cmd`     | Commands executed against DESFire tags              |\n| `buf`     | Creation and manipulation of byte sequence buffers  |\n| `key`     | Creation of DESFire key objects                     |\n| `crc`     | Checksum functions                                  |\n| `crypto`  | Cryptographic functions                             |\n| `show`    | Compound functions for analyzing tags               |\n\n### DESFire Commands\n\nThe `cmd`-namespace contains all commands which are supported by DESFire tags.\nAll commands receive an provide the same parameters as specified in the DESFire\nstandard. A detailed explantion of the DESFire standard is beyond the scope of\nthis document.\n\nThe remainder of this section shows a sample session. The tag used is based on\nthe example script `examples/clt21.lua`. You can prepare you own card by\nexecuting this script inside the DESFire shell.\n\nActivate debugging.\n\n```\n\u003e debugset(255)\n```\n\nShow all present DESFire applications.\n\n```\n\u003e cmd.appids()\n\u003e\u003e\u003e GetApplicationIDs \u003c\u003c\u003c\n    STAT \u003c=\u003e 0: OK\n     AID \u003c=  0x000001\n```\n\nChange current application to AID 1.\n\n```\n\u003e cmd.select(1)\n\u003e\u003e\u003e SelectApplication \u003c\u003c\u003c\n     AID  =\u003e 0x000001\n    STAT \u003c=\u003e 0: OK\n```\n\nShow all files of this application.\n\n```\n\u003e cmd.fileids()\n\u003e\u003e\u003e GetFileIDs \u003c\u003c\u003c\n    STAT \u003c=\u003e 0: OK\n     FID \u003c=  0\n     FID \u003c=  1\n     FID \u003c=  2\n     FID \u003c=  3\n     FID \u003c=  4\n```\n\nThere are five files defined numbered from 0 through 4.\n\nShow settings of file 0.\n\n```\n\u003e cmd.gfs(0) -- get file settings\n\u003e\u003e\u003e GetFileSettings \u003c\u003c\u003c\n     FID  =\u003e 0\n    STAT \u003c=\u003e 0: OK\n    COMM \u003c=  0x03 (CRYPT)\n     ACL \u003c=  RD:01 WR:02 RW:03 CA:00\n    FSET \u003c=  [SDF]  SIZE: 32\n```\n\nIt is a standard data file of 32 bytes length. File access operations are\nencrypted. Key 1 is allowed to read that file, key 2 is allowed to write,\nkey 3 is allowd to read and write. Key 0 is allowed to change these access\nsettings.\n\nTry to to read the file 0.\n\n```\n\u003e cmd.read(0, 0, 32)\n\u003e\u003e\u003e ReadData \u003c\u003c\u003c\n     FID  =\u003e 0\n     OFF  =\u003e 0\n     LEN  =\u003e 32\n         *I* Executing GetFileSettings() to determine file type and size.\n    STAT \u003c=\u003e 174: AUTHENTICATION_ERROR\n```\n\nThis operations fails as we are unauthenticated.\n\nAuthenticate with key 1.\n\n```\n\u003e cmd.auth(1, AES(\"11111111111111111111111111111111\"))\n\u003e\u003e\u003e Authenticate \u003c\u003c\u003c\n     KNO  =\u003e 1\n     KEY  =\u003e AES:11111111111111111111111111111111 (V:000)\n    STAT \u003c=\u003e 0: OK\n```\n\nNow try to read file 0 again.\n\n```\n\u003e cmd.read(0, 0, 32)\n\u003e\u003e\u003e ReadData \u003c\u003c\u003c\n     FID  =\u003e 0\n     OFF  =\u003e 0\n     LEN  =\u003e 32\n         *I* Executing GetFileSettings() to determine file type and size.\n    STAT \u003c=\u003e 0: OK\n     BUF \u003c=  00000000  43 68 65 6d  6e 69 74 7a  |Chemnitz|\n     BUF \u003c=  00000008  65 72 00 00  00 00 00 00  |er......|\n     BUF \u003c=  00000010  00 00 00 00  00 00 00 00  |........|\n     BUF \u003c=  00000018  00 00 00 00  00 00 00 00  |........|\n```\n\nAs we gained read permission, the command succeeds.\n\nWriting to the file is disallowed in the current authentication status. To\nwrite to the file, we authenticate using key number 3.\n\n```\n\u003e cmd.write(0, 11, buf.fromascii(\"Linux-Tage\"))\n\u003e\u003e\u003e WriteData \u003c\u003c\u003c\n     FID  =\u003e 0\n     OFF  =\u003e 11\n     BUF  =\u003e 0000000b  4c 69 6e 75  78 2d 54 61  |Linux-Ta|\n     BUF  =\u003e 00000013  67 65                     |ge      |\n    STAT \u003c=\u003e 174: AUTHENTICATION_ERROR\n\u003e cmd.auth(3, AES(\"33333333333333333333333333333333\"))\n\u003e\u003e\u003e Authenticate \u003c\u003c\u003c\n     KNO  =\u003e 3\n     KEY  =\u003e AES:33333333333333333333333333333333 (V:000)\n    STAT \u003c=\u003e 0: OK\n\u003e cmd.write(0, 11, buf.fromascii(\"Linux-Tage\"))\n\u003e\u003e\u003e WriteData \u003c\u003c\u003c\n     FID  =\u003e 0\n     OFF  =\u003e 11\n     BUF  =\u003e 0000000b  4c 69 6e 75  78 2d 54 61  |Linux-Ta|\n     BUF  =\u003e 00000013  67 65                     |ge      |\n    STAT \u003c=\u003e 0: OK\n```\n\nAs we are authenticated using key number 3, reading the file is allowed, too.\n\n```\n\u003e cmd.read(0, 0, 32)\n\u003e\u003e\u003e ReadData \u003c\u003c\u003c\n     FID  =\u003e 0\n     OFF  =\u003e 0\n     LEN  =\u003e 32\n         *I* Executing GetFileSettings() to determine file type and size.\n    STAT \u003c=\u003e 0: OK\n     BUF \u003c=  00000000  43 68 65 6d  6e 69 74 7a  |Chemnitz|\n     BUF \u003c=  00000008  65 72 00 4c  69 6e 75 78  |er.Linux|\n     BUF \u003c=  00000010  2d 54 61 67  65 00 00 00  |-Tage...|\n     BUF \u003c=  00000018  00 00 00 00  00 00 00 00  |........|\n```\n\nFinally we inspect the security settings of the currently selected application.\n\n```\n\u003e cmd.gks()\n\u003e\u003e\u003e GetKeySettings \u003c\u003c\u003c\n    STAT \u003c=\u003e 0: OK\n  KEYSET \u003c=  0x0f = AKC:AMK CONF:M CA/F:* DA/F:M/* LIST:* MKC:M\n MAXKEYS \u003c=  4\n```\n\nThe application has four keys. The key settings are encoded in the key settings\nbyte `0x0f` and interpreted as following.\n\n* `AKC:AMK`: Each application key (1 through 3) can be changed by the\n  application master key (key number 0).\n* `CONF:M`: The security settings can by changed by the application master key.\n* `CA/F:*`: Files can be created unauthenticaed.\n* `DA/F:M/*`: Files can be deleted unauthenticated. The application can be\n  deleted by the applications master key.\n* `LIST:*`: File listing is permitted without authentication.\n* `MKC:M`: The application master key (key number 0) can be changed by itself.\n\nFor a detailed description of the security settings, refer to the DESFire\nspecification.\n\n### Byte Sequence Buffers\n\nAccess to files is byte oriented. The file content is stored in buffer objects\nwhich can be transformed to different representations:\n\n* UTF-8 text strings\n* Hex strings\n* Lua tables\n\nThis expression creates a buffer object `x` from the text string `Hello\nWorld!`.\n\n```\n\u003e x = buf.fromascii(\"Hello World!\")\n```\n\nThis buffer can be converted into a hex string ...\n\n```\n\u003e print(x:tohexstr())\n48656c6c6f20576f726c6421\n```\n\n... or into a Lua table ...\n\n```\n\u003e for i, v in ipairs(x:totable()) do\n\u003e\u003e   print(i, v)\n\u003e\u003e end\n1       72\n2       101\n3       108\n4       108\n5       111\n6       32\n7       87\n8       111\n9       114\n10      108\n11      100\n12      33\n```\n\n... or into a hex dump.\n\n```\n\u003e print(x:hexdump())\n00000000  48 65 6c 6c  6f 20 57 6f  |Hello Wo|\n00000008  72 6c 64 21               |rld!    |\n```\n\n### Key Objetcs\n\nCryptographic key values are stored in lua tables. These key objects are\nmanipulated by functions of the `key`-namespace. Each key has a type, a key\nvalue and a key version number. The following code creates the AES-key\n`00112233445566778899aabbccddeeff` with key version 0.\n\n```\nk = key.create({ t = \"AES\", k = \"00112233445566778899aabbccddeeff\", v = 0 })\n```\n\nThe shortcut function `AES()` simplifies this expression considerably.\n\n```\nk = AES(\"00112233445566778899aabbccddeeff\", 0)\n```\n\nIf you skip the key version number, it will default to 0.\n\n```\nk = AES(\"00112233445566778899aabbccddeeff\")\n```\n\nIf you skip the key value, the key value will be set to zero. This is the\ndefault key.\n\n```\nk = AES()\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhamarituc%2Fdesfsh","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhamarituc%2Fdesfsh","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhamarituc%2Fdesfsh/lists"}