{"id":27641398,"url":"https://github.com/raphaelm/pysbtse","last_synced_at":"2026-02-08T11:40:13.592Z","repository":{"id":260541635,"uuid":"881596870","full_name":"raphaelm/pysbtse","owner":"raphaelm","description":"Python wrapper to access Swissbit TSE","archived":false,"fork":false,"pushed_at":"2024-12-25T19:04:05.000Z","size":72,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-12-25T20:19:40.401Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/raphaelm.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"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":"2024-10-31T22:16:05.000Z","updated_at":"2024-12-25T19:04:08.000Z","dependencies_parsed_at":null,"dependency_job_id":"aa0e97dd-5d6e-4447-855f-fdb354b90ad9","html_url":"https://github.com/raphaelm/pysbtse","commit_stats":null,"previous_names":["raphaelm/pysbtse"],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raphaelm%2Fpysbtse","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raphaelm%2Fpysbtse/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raphaelm%2Fpysbtse/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raphaelm%2Fpysbtse/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/raphaelm","download_url":"https://codeload.github.com/raphaelm/pysbtse/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250535086,"owners_count":21446504,"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","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":[],"created_at":"2025-04-23T23:47:02.481Z","updated_at":"2026-02-08T11:40:08.534Z","avatar_url":"https://github.com/raphaelm.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pysbtse\n\nPython bindings and command-line tool for the Swissbit TSE. \n\n## Setup\n\nInstall ``sbtse`` like any Python package, e.g. with pip. Additionally, you need ``libWormAPI.so`` for your architecture\nin your library path or your working directory. We are not allowed to distribute this library here, so please try to\nfind it on the internet or request it from a Swissbit TSE seller.\n\n## Capabilities\n\nThis module includes an auto-generated ctypes wrapper for the `libWormAPI.so` from Swissbit SDK 5.9.1.\nOn top of that, it includes a high-level Python interface to work with the TSE.\nThe Python interface supports all features of the offline SDK except for:\n\n- Online firmware updates and manual firmware transfer (bundled firmware updates are supported)\n- Incremental TAR exports\n- Export lifetime monitoring information\n\nLAN TSE support is implemented but not tested.\n\n\n## Command line usage\n\n```\nUsage: sbtse [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --path DIRECTORY  TSE mount point\n  --url TEXT        LAN TSE URL\n  --api-key TEXT    LAN TSE API Key\n  --tse TEXT        LAN TSE serial number\n  --help            Show this message and exit.\n\nCommands:\n  config           Manipulate TSE configuration\n  delete           Delete stored data.\n  entries          Query log entries\n  export           Export stored data.\n  factory-reset    Factory reset (development TSE only)\n  firmware-update  Update firmware to version bundled with SDK.\n  info             Show info and flash health status\n  mock             Run mock API server (development only)\n  pin              Manage Admin PIN\n  puk              Manage PUK\n  selftest         Run self-test\n  serve            Run local API server\n  setup            Run setup procedure for a fresh TSE\n  time-admin-pin   Manage Time Admin PIN\n  transaction      Create and query transactions\n```\n\nRun ``sbtse --path /mnt/tse COMMAND --help`` for the options and subcommands of the commands.\n\n## Python library usage\n\nExample:\n\n```python\nfrom sbtse import worm, errors\n\nclient_id = \"TEST\"\nadmin_pin = \"12345\"\nadmin_puk = \"123456\"\ntime_admin_pin = \"12345\"\n\nprint(\"SDK version:\", worm.get_version())\n\nwith worm.LocalWormContext(\"/mnt/tse/\") as w:\n    info = w.info()\n    print(\"Info:\", info)\n    print(\"Initial credentials:\", w.derive_initial_credentials())\n    print(\"Running self test...\")\n    try:\n        w.run_self_test(client_id)\n    except errors.WormErrorClientNotRegistered:\n        print(\"Not registered.\")\n        if info[\"hasChangedAdminPin\"]:\n            w.login_as_admin(admin_pin)\n            w.register_client(client_id)\n        else:\n            w.setup(client_id, admin_pin, admin_puk, time_admin_pin)\n        \n    if w.bundled_firmware_update_available():\n        print(\"Updating firmware...\")\n        w.bundled_firmware_update_apply()\n    \n    print(\"Flash health:\", w.flash_health())\n    w.login_as_time_admin(time_admin_pin)\n    w.update_time()\n    \n    print(\"Registered clients:\", w.list_registered_clients())\n    \n    # Transaction handling\n    print(\"Performing transaction...\")\n    tx = w.transaction_start(client_id, \"\", \"\")\n    print(\"Started transactions:\", w.list_started_transactions())\n    print(\"Finished transaction:\", w.transaction_finish(client_id, tx[\"transactionNumber\"], \"Foobar\", \"Kassenbeleg\"))\n    \n    # Export capabilities\n    print(\"Last transaction:\", w.last_transaction())\n    for tx in w.iterate_entries():\n        print(\"Entry:\", tx)\n    print(\"Certificate:\", w.get_log_message_certificate())\n    \n    print(\"Exporting TAR…\")\n    with open(\"export.tar\", \"wb\") as f:\n        w.export_tar(f)\n\n    print(\"Exporting filtered TAR…\")\n    with open(\"export_tx_filtered.tar\", \"wb\") as f:\n        w.export_tar(f, start_transaction=0, end_transaction=2, client_id=client_id)\n```\n\nThe example does not show all features. Have a look at ``help(LocalWormContext)`` for a full list of methods.\n\nFor LAN TSE (untested):\n\n```python\nfrom sbtse import worm\n\nwith worm.LANWormContext(\"https://10.1.1.1:9000\", \"api_key\") as w:\n    tses = w.list_connected_tses()\n    print(\"TSEs:\", tses)\n    w.select_tse(tses[0])\n    with w.lock_tse():\n        w.setup(...)\n    ...\n```\n\n## API Usage\n\nThe API is executed as a single-thread single-process worker to avoid concurrent access to the TSE which might be problematic.\nHowever, this also means that the API might be slow to respond under concurrent access. This is intentional.\n\n| Method | Path                                                                           | Description                                      |\n| --- |--------------------------------------------------------------------------------|--------------------------------------------------|\n| GET | [/info](#getinfo)                                                              | Retrieve information about the TSE               |\n| GET | [/health](#gethealth)                                                          | Retrieve health status information about the TSE |\n| GET | [/certificate](#getcertificate)                                                | Retrieve the certificate used for signing        |\n| POST | [/transactions/](#posttransactions)                                            | Start a transcation                              |\n| POST | [/transactions/{transaction_id}/update](#posttransactionstransaction_idupdate) | Update a transaction                             |\n| POST | [/transactions/{transaction_id}/finish](#posttransactionstransaction_idfinish) | Finish a transaction                             |\n\n### [GET] /info\n\nRetrieve information about the TSE\n\n#### Responses\n\n- 200 Successful Response\n\n`application/json`\n\n```ts\n{\n  isDevelopmentFirmware: boolean\n  capacity: integer\n  size: integer\n  hasValidTime: boolean\n  hasPassedSelfTest: boolean\n  isCtssInterfaceActive: boolean\n  isExportEnabledIfCspTestFails: boolean\n  initializationState: string\n  hasChangedPuk: boolean\n  hasChangedAdminPin: boolean\n  timeUntilNextSelfTest: integer\n  startedTransactions: integer\n  maxStartedTransactions: integer\n  createdSignatures: integer\n  maxSignatures: integer\n  remainingSignatures: integer\n  maxTimeSynchronizationDelay: integer\n  maxUpdateDelay: integer\n  tsePublicKey: string\n  timeUntilNextTimeSynchronization: integer\n  tseSerialNumberBytes: string\n  tseSerialNumberHex: string\n  tseDescription: string\n  registeredClients: integer\n  maxRegisteredClients: integer\n  certificateExpirationDate: string\n  tarExportSizeInSectors: integer\n  tarExportSize: integer\n  hardwareVersion: integer\n  softwareVersion: integer\n  formFactor: string\n  logTimeFormat: str\n  signatureAlgorithm: str\n}\n```\n\n### [GET] /health\n\nRetrieve health information about the TSE\n\n#### Responses\n\n- 200 Successful Response\n\n`application/json`\n\n```ts\n{\n  uncorrectableEccErrors: integer\n  percentageRemainingSpareBlocks: integer\n  percentageRemainingEraseCounts: integer\n  percentageRemainingTenYearsDataRetention: integer\n  needsReplacement: boolean\n}\n```\n\n### [GET] /certificate\n\nRetrieve the certificate used for signing\n\n### [POST] /transactions/\n\nStart a transaction\n\n#### Request body\n\n- application/json\n\n```ts\n{\n  client_id: string\n  process_data: string\n  process_type: string\n}\n```\n\n#### Responses\n\n- 200 Successful Response\n\n`application/json`\n\n```ts\n{\n  logTime: integer\n  serialNumberHex: string\n  signatureCounter: integer\n  transactionNumber: integer\n  signatureBase64: string\n}\n```\n\n### [POST] /transactions/{transaction_id}/update\n\nUpdate a transaction\n\n#### Request body\n\n- application/json\n\n```ts\n{\n  client_id: string\n  process_data: string\n  process_type: string\n}\n```\n\n#### Responses\n\n- 200 Successful Response\n\n`application/json`\n\n```ts\n{\n  logTime: integer\n  serialNumberHex: string\n  signatureCounter: integer\n  transactionNumber: integer\n  signatureBase64: string\n}\n```\n\n### [POST] /transactions/{transaction_id}/finish\n\nFinish a transaction\n\n#### Request body\n\n- application/json\n\n```ts\n{\n  client_id: string\n  process_data: string\n  process_type: string\n}\n```\n\n#### Responses\n\n- 200 Successful Response\n\n`application/json`\n\n```ts\n{\n  logTime: integer\n  serialNumberHex: string\n  signatureCounter: integer\n  transactionNumber: integer\n  signatureBase64: string\n}\n```\n\n\n## License\n\nThe code in this library is licensed under the Apache 2.0 license.\nNote that the binary library and documentation provided by Swissbit is provided under the \"Swissbit Device Driver Adaptation \u0026 Distribution License\" and therefore not shared in this repository.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fraphaelm%2Fpysbtse","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fraphaelm%2Fpysbtse","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fraphaelm%2Fpysbtse/lists"}