{"id":25227717,"url":"https://github.com/dafvid/pylibtls","last_synced_at":"2025-10-26T04:32:50.055Z","repository":{"id":46481729,"uuid":"415097427","full_name":"dafvid/pylibtls","owner":"dafvid","description":"Python bindings for LibreSSL libtls","archived":false,"fork":false,"pushed_at":"2023-02-02T10:51:23.000Z","size":55,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-01T00:29:17.736Z","etag":null,"topics":["libtls","python","tls"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dafvid.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}},"created_at":"2021-10-08T18:58:55.000Z","updated_at":"2021-12-30T14:29:08.000Z","dependencies_parsed_at":"2023-02-17T18:46:08.099Z","dependency_job_id":null,"html_url":"https://github.com/dafvid/pylibtls","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/dafvid/pylibtls","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dafvid%2Fpylibtls","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dafvid%2Fpylibtls/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dafvid%2Fpylibtls/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dafvid%2Fpylibtls/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dafvid","download_url":"https://codeload.github.com/dafvid/pylibtls/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dafvid%2Fpylibtls/sbom","scorecard":{"id":316772,"data":{"date":"2025-08-11","repo":{"name":"github.com/dafvid/pylibtls","commit":"dfcfdfa25af46b641e3da1142749ce99605c7265"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":0,"reason":"Found 1/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 2 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-18T00:22:21.091Z","repository_id":46481729,"created_at":"2025-08-18T00:22:21.092Z","updated_at":"2025-08-18T00:22:21.092Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":281059645,"owners_count":26437056,"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-10-26T02:00:06.575Z","response_time":61,"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":["libtls","python","tls"],"created_at":"2025-02-11T09:09:03.532Z","updated_at":"2025-10-26T04:32:49.816Z","avatar_url":"https://github.com/dafvid.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pylibtls\n[![PyPI version](https://badge.fury.io/py/pylibtls.svg)](https://badge.fury.io/py/pylibtls)\n\n## About\nDeveloped initially in september 2021 on FreeBSD 13.0 with LibreSSL 3.3.3 with API Version `20200120`. \n\nThe aim is to just wrap the API as thinly as possible. A few principles: \n- `str` is encoded using default encoding (just calling `encode()`)\n- Epochs are converted to UTC datetime\n- Return code `-1` is made into `TLSError`\n- Returned `1`s and `0`s are cast to `boolean`\n- Returned strings are converted with `decode()`\n- `tls_read()` and `tls_write()` expects `bytes` though\n- The order of the functions defined matches that of libtls.h\n- Argument names are not always pythonic but matches that of libtls.h\n\n## Background\nI always thought it was a bit of a mistake for [LibreSSL](https://www.libressl.org/index.html) to be an drop-in replacement for OpenSSL. I just wanted to use libtls and be done with it. But since LibreSSL always replaced OpenSSL and that always seemed to be problematic I looked for ways to install just libtls, but to no awail. Until April 18, 2021 when version 3.3.2 of LibreSSL was released.\n\nFrom the [release notes](https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-3.3.2-relnotes.txt) (way down): \n\u003eAdded '--enable-libtls-only' build option, which builds and installs a statically-linked libtls, skipping libcrypto and libssl. This is useful for systems that ship with OpenSSL but wish to also package libtls.\n\n*YEY!*\n\nSome time after it was made a [flavor](https://docs.freebsd.org/en/books/porters-handbook/flavors/) of the FreeBSD LibreSSL [port](https://www.freshports.org/security/libressl/). So now I finally had it! So I started looking for Python wrappers for it. I found [python-libtls](https://pypi.org/project/python-libtls/) by Vinay Sajip. Last update in 2017, looked abandoned, so [I made a new one](https://www.youtube.com/channel/UCMrMVIBtqFW6O0-MWq26gqw).\n\n## Getting started\n### Getting libtls\nFirst thing is getting libtls somehow. If you already have LibreSSL you should be good to go. Otherwise hope the **\u0026#x2011;\u0026#x2011;enable\u0026#x2011;libtls\u0026#x2011;only** build flag is used somehow in whatever package thingamajig you're using.\n#### FreeBSD\n```sh\npkg install libressl-libtls\n```\n#### MacOS\nI was suprised to find out Apple deprecated OpenSSL after High Sierra and now ships with LibreSSL. But you can't use it =/. But install it with Homebrew!\n```zsh\nbrew install libressl\n```\nIt's not linked since it would mess with the one shipped with MacOS. Set the env variable `PYLIBTLS_LIBTLS_PATH` to `/usr/local/opt/libressl/lib/libtls.dylib` and you're good to go.\n\n#### Linux (Rocky Linux 8.4)\nThere's no love for libtls in the Linux community! So no package in rpm. Beware of the package `libretls`, it's libtls on top of OpenSSL!\nBut thankfully it's pretty easy to compile it yourself. This is how I installed it on Rocky Linux, YMMW. Instructions @ [GitHub](https://github.com/libressl-portable/portable)\n```sh\ndnf install wget\nwget https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-3.3.5.tar.gz\ncd libressl-3.3.5\n./configure --enable-libtls-only  # very important flag!\nmake check  # as recommended on GitHub\nmake install\n```\nNow it's installed in `/usr/local/lib` but you need to tell the linker that. There's a few ways to do that. I added the path to `/etc/ld.so.con`. You can also add it to env var `LD_LIBRARY_PATH`.\n\nThen you need to tell libtls how to find your CA bundle. The default path is apparently hard coded to `/etc/ssl/cert.pem`. This is NOT where Rocky Linux keeps them, so I soft linked it like so:\n```sh\nln -s /etc/pki/tls/cert.pem /etc/ssl/cert.pem\n```\nThis all depends on your distro. All RHEL derivaties keep their bundle in `/etc/pki/tls/cert.pem`.\n\n#### Environment variables\nThere's an env variable you can use to specify the path to libtls if `ctypes` is unable to find it automagically and that's `PYLIBTLS_LIBTLS_PATH`.\n\n### Getting pylibtls\nJust use pip:\n```sh\n$\u003e pip install pylibtls\n```\nand in your script\n```py\nimport tls\n```\n\n## Usage\nOh the fun part!\n\nFunctions are named the same so `tls_init()` is `tls.tls_init()` and so on. Constants from header file are just `tls.TLS_A_CONSTANT`.\n\n```python\nfrom tls import (tls_config_new, tls_client, tls_configure, tls_connect, tls_write, \n                tls_read, tls_config_free, tls_close, tls_free)\n\ncfg = tls_config_new()\nctx = tls_client()\ntls_configure(ctx, cfg)\n\nhost = 'www.openbsd.org'\ntls_connect(ctx, host, 443)\nquery = \"HEAD / HTTP/1.0\\r\\nHost: {}\\r\\n\\r\\n\".format(host)\ntls_write(ctx, query.encode())\nr = tls_read(ctx)\nprint(r.decode())\n\ntls_config_free(cfg)\ntls_close(ctx)\ntls_free(ctx)\n```\n\nThe full monty\n\n```python\nfrom tls import *\n\nprint('Version:', TLS_API)\ncfg = tls_config_new()\ntls_config_set_ca_file(cfg, \"/etc/ssl/cert.pem\")\nprint(tls_default_ca_cert_file())\ntls_config_set_protocols(cfg, TLS_PROTOCOL_TLSv1_2)\nctx = tls_client()\ntls_configure(ctx, cfg)\n\nhost = 'www.openbsd.org'\nprint('host:', host)\n\nprint('connect_socket')\ntls_connect(ctx, host, 443)\ntls_handshake(ctx)\nprint(\"Cert provided:\", tls_peer_cert_provided(ctx))\nprint(\"Hash (SHA256):\", tls_peer_cert_hash(ctx))\nprint(\"Issuer:\", tls_peer_cert_issuer(ctx))\nprint(\"Subject:\", tls_peer_cert_subject(ctx))\nprint(\"NotBefore (UTC):\", tls_peer_cert_notbefore(ctx))\nprint(\"NotAfter (UTC):\", tls_peer_cert_notafter(ctx))\nprint(\"ALPN:\", tls_conn_alpn_selected(ctx))\nprint(\"Cipher:\", tls_conn_cipher(ctx))\nprint(\"Servername:\", tls_conn_servername(ctx))\nprint(\"Resumed:\", tls_conn_session_resumed(ctx))\nprint(\"TLS Version:\", tls_conn_version(ctx))\nprint(\"OCSP URL:\", tls_peer_ocsp_url(ctx))\nprint(\"OCSP result:\", tls_peer_ocsp_result(ctx))\nif tls_peer_ocsp_result(ctx) is not None:\n    print(\"OCSP Response Status:\", TLS_OCSP_RESPONSE[tls_peer_ocsp_response_status(ctx)])\n    print(\"OCSP Cert Status:\", TLS_OCSP_CERT[tls_peer_ocsp_cert_status(ctx)])\n    print(\"OCSP CRL Reason:\", TLS_CRL_REASON[tls_peer_ocsp_crl_reason(ctx)])\n    print(\"OCSP revocation:\", tls_peer_ocsp_revocation_time(ctx))\n    print(\"OCSP this update:\", tls_peer_ocsp_this_update(ctx))\n    print(\"OCSP next update:\", tls_peer_ocsp_next_update(ctx))\n\nprint()\nquery = \"HEAD / HTTP/1.0\\r\\nHost: {}\\r\\n\\r\\n\".format(host)\nprint('tls_write', query)\nr = tls_write(ctx, query.encode())\nprint(r, 'bytes')\nprint('read')\nr = tls_read(ctx)\nprint(len(r), 'bytes')\nprint(r.decode())\n\ntls_config_free(cfg)\ntls_close(ctx)\ntls_free(ctx)\n```\nThis is using the extra `dict`s I put in for reverse lookup of values-to-name: `TLS_OCSP_RESPONSE`, `TLS_OCSP_CERT` and `TLS_CRL_REASON`. They require that OCSP stapling is active on the server in question. In the example, it is.\n\nVery simple server\n```py\nfrom tls import *\nimport socket\n\n\ncfg = tls_config_new()\ntls_config_set_keypair_file(cfg, 'cert.pem', 'privkey.pem')\n\nctx = tls_server()\ntls_configure(ctx, cfg)\n\ns = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\ns.bind(('www.example.com', 2345))\ns.listen(10)\nprint('Listening on socket...')\nc, addr = s.accept()\nprint(addr, 'connected')\ncctx = tls_accept_socket(ctx, c)\ntls_write(cctx, 'Hello World'.encode())\nprint(tls_read(cctx))\ntls_close(cctx)\ntls_free(cctx)\n\ntls_config_free(cfg)\ntls_free(ctx)\n\ns.close()\n```\nAccepts a single connection and writes `Hello World` then reads once and shuts down.\n\n## Documentation\nNone yet, apart from this README. See the [OpenBSD documentation](https://man.openbsd.org/tls_init.3) for reference. It should get you up and running somewhat.\n\n## Status\n#### 2021-10-17\nInstructions for Linux (Rocky Linux 8.4)\n#### 2021-10-13\nInstructions for simple server\n#### 2021-10-09\nPublished on [PyPi](https://pypi.org/project/pylibtls/)!\n#### 2021-10-08\nFirst pushed to GitHub (A bit nervous). Most of the API implemented. Only client functionality tested. No local OCSP-stuff (getting the staple file is HARD). Only tested on FreeBSD. Should work fine on Linux at least. No `libtls-only` brew Formulae so MacOS is out (might be next project). Windows seems to be a sad chapter in general. Vinay stranded [here](https://github.com/libressl-portable/portable/issues/266) more or less.\n\n## TODO\n- [ ] All `mem`-functions that read stuff from memory loaded with `tls_load_file()`\n- [ ] Callbacks versions of `tls_accept()` and `tls_connect()`\n- [ ] File descriptor versions of the same\n- [ ] `tls_peer_cert_chain_pem()`\n- [ ] `assert` a few things here and there\n\n## Acknowledgments\n- [python-libtls](https://bitbucket.org/vinay.sajip/python-libtls/src/master/) and [python-gnutls](https://github.com/AGProjects/python-gnutls) for inspiration\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdafvid%2Fpylibtls","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdafvid%2Fpylibtls","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdafvid%2Fpylibtls/lists"}