{"id":13413511,"url":"https://github.com/hyperboloide/lk","last_synced_at":"2026-02-01T14:07:57.095Z","repository":{"id":42520407,"uuid":"63350884","full_name":"hyperboloide/lk","owner":"hyperboloide","description":"Simple licensing library for golang.","archived":false,"fork":false,"pushed_at":"2025-12-20T05:35:26.000Z","size":73,"stargazers_count":403,"open_issues_count":0,"forks_count":64,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-12-22T12:37:23.932Z","etag":null,"topics":["crypto","golang","sign"],"latest_commit_sha":null,"homepage":"","language":"Go","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/hyperboloide.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}},"created_at":"2016-07-14T16:06:07.000Z","updated_at":"2025-12-20T08:39:08.000Z","dependencies_parsed_at":"2024-01-08T15:34:45.086Z","dependency_job_id":null,"html_url":"https://github.com/hyperboloide/lk","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/hyperboloide/lk","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hyperboloide%2Flk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hyperboloide%2Flk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hyperboloide%2Flk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hyperboloide%2Flk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hyperboloide","download_url":"https://codeload.github.com/hyperboloide/lk/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hyperboloide%2Flk/sbom","scorecard":{"id":475197,"data":{"date":"2025-08-11","repo":{"name":"github.com/hyperboloide/lk","commit":"ce3fecd3479808bf05c6dab156a24651370acbcd"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.5,"checks":[{"name":"Code-Review","score":0,"reason":"Found 2/26 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":"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":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","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":"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":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/tests.yml:1","Info: no jobLevel write permissions found"],"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":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/tests.yml:10: update your workflow using https://app.stepsecurity.io/secureworkflow/hyperboloide/lk/tests.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/tests.yml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/hyperboloide/lk/tests.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/tests.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/hyperboloide/lk/tests.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/tests.yml:25: update your workflow using https://app.stepsecurity.io/secureworkflow/hyperboloide/lk/tests.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/tests.yml:27: update your workflow using https://app.stepsecurity.io/secureworkflow/hyperboloide/lk/tests.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/tests.yml:36: update your workflow using https://app.stepsecurity.io/secureworkflow/hyperboloide/lk/tests.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/tests.yml:39: update your workflow using https://app.stepsecurity.io/secureworkflow/hyperboloide/lk/tests.yml/master?enable=pin","Info:   0 out of   5 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   2 third-party GitHubAction dependencies pinned"],"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":"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 'master'"],"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 9 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"}},{"name":"Vulnerabilities","score":0,"reason":"12 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GO-2022-0969 / GHSA-69cg-p879-7622","Warn: Project is vulnerable to: GO-2023-1495 / GHSA-fxg5-wq6x-vr4w","Warn: Project is vulnerable to: GO-2022-1144 / GHSA-xrjj-mj9h-534m","Warn: Project is vulnerable to: GO-2023-1571 / GHSA-vvpx-j8f3-3w6h","Warn: Project is vulnerable to: GO-2023-1988 / GHSA-2wrh-6pvc-2jm9","Warn: Project is vulnerable to: GO-2023-2102 / GHSA-4374-p667-p6c8","Warn: Project is vulnerable to: GHSA-qppj-fm5r-hxr3","Warn: Project is vulnerable to: GO-2024-2687 / GHSA-4v7x-pqxf-cx7m","Warn: Project is vulnerable to: GO-2024-3333","Warn: Project is vulnerable to: GO-2025-3503 / GHSA-qxp5-gwg8-xv66","Warn: Project is vulnerable to: GO-2025-3595 / GHSA-vvgc-356p-c3xw","Warn: Project is vulnerable to: GO-2022-1059 / GHSA-69ch-w2m2-3vjp"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-19T15:06:47.204Z","repository_id":42520407,"created_at":"2025-08-19T15:06:47.204Z","updated_at":"2025-08-19T15:06:47.204Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28980192,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-01T13:38:33.235Z","status":"ssl_error","status_checked_at":"2026-02-01T13:38:32.912Z","response_time":56,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["crypto","golang","sign"],"created_at":"2024-07-30T20:01:42.028Z","updated_at":"2026-02-01T14:07:57.081Z","avatar_url":"https://github.com/hyperboloide.png","language":"Go","readme":"# license-key\n\n[![Tests](https://github.com/hyperboloide/lk/actions/workflows/tests.yml/badge.svg)](https://github.com/hyperboloide/lk/actions/workflows/tests.yml)\n[![Go Reference](https://pkg.go.dev/badge/github.com/hyperboloide/lk.svg)](https://pkg.go.dev/github.com/hyperboloide/lk)\n[![Go Report Card](https://goreportcard.com/badge/github.com/hyperboloide/lk)](https://goreportcard.com/report/github.com/hyperboloide/lk)\n\nA simple licensing library in Golang, that generates license files containing\narbitrary data (ex: user email, end date...) that you can further validate if you want.\n\nThe license files are signed with the: Elliptic Curve Digital Signature Algorithm \n(using [elliptic.P384 ](https://pkg.go.dev/crypto/elliptic#P384)) \n\nThe license file can be marshalled in an easy to distribute format (ex: base32 encoded strings)\n\nNote that this implementation is quite basic and that in no way it could\nprevent someone to hack your software. The goal of this project is only\nto provide a convenient way for software publishers to generate license keys\nand distribute them without too much hassle for the user.\n\n### How does it works?\n\n1. Generate a private key (and keep it secure).\n2. Transform the data you want to provide (end date, user email...) to a byte array (using json or gob for example).\n3. The library takes the data and create a cryptographically signed hash that is appended to the data.\n4. Convert the result to a Base64/Base32/Hex string and send it to the end user: this is the license.\n5. when the user starts your program load the license and verify the signature using a public key.\n6. validate the data in your license key (ex: the end date)\n\n\n### lkgen\n\nA command line helper [lkgen](lkgen) is also provided to generate private keys and create licenses.\n\nThis is also a good example of how to use the library.\n\n### Examples\n\n#### Generating a new license:\n\nBelow is an example of code that generates a license from a private key and a struct containing the end date and a user email that is marshalled to json.\n\n```go\n// first, you need a base32 encoded private key generated by `lkgen gen` note that you might\n// prefer reading it from a file, and that it should stay secret (ie: dont distribute it with your app)!\nconst privateKeyBase32 = \"FD7YCAYBAEFXA22DN5XHIYLJNZSXEAP7QIAACAQBANIHKYQBBIAACAKEAH7YIAAAAAFP7AYFAEBP7BQAAAAP7GP7QIAWCBCRKQVWKPT7UJDNP4LB5TXEQMO7EYEGDCE42KVBDNEGRIYIIJFBIWIVB6T6ZTKLSYSGK54DZ5VX6M5SJHBYZU2JXUFXJI25L2JJKJW4RL7UL2XBDT4GKYZ5IS6IWBCN7CWTMVBCBHJMH3RHZ5BVGVAY66MQAEYQEPSS2ANTYZIWXWSGIUJW3MDOO335JK3D4N3IV4L5UTAQMLS5YC7QASCAAUOHTZ5ZCCCYIBNCWBELBMAA====\"\n\n// Unmarshal the private key\nprivateKey, err := lk.PrivateKeyFromB32String(privateKeyBase32)\nif err != nil {\n\tlog.Fatal(err)\n}\n\n// Define the data you need in your license,\n// here we use a struct that is marshalled to json, but ultimately all you need is a []byte.\ndoc := struct {\n\tEmail string    `json:\"email\"`\n\tEnd   time.Time `json:\"end\"`\n}{\n\t\"user@example.com\",\n\ttime.Now().Add(time.Hour * 24 * 365), // 1 year\n}\n\n// marshall the document to []bytes (this is the data that our license will contain).\ndocBytes, err := json.Marshal(doc)\nif err != nil {\n\tlog.Fatal(err)\n}\n\n// generate your license with the private key and the document\nlicense, err := lk.NewLicense(privateKey, docBytes)\nif err != nil {\n\tlog.Fatal(err)\n\n}\n// the b32 representation of our license, this is what you give to your customer.\nlicenseB32, err := license.ToB32String()\nif err != nil {\n\tlog.Fatal(err)\n}\nfmt.Println(licenseB32)\n```\n\n#### Validating a license:\n\nBefore your execute your program you want to check the user license, here is how with key generated from the previous example:\n\n```go\n// A previously generated license b32 encoded. In real life you should read it from a file...\nconst licenseB32 = \"FT7YOAYBAEDUY2LDMVXHGZIB76EAAAIDAECEIYLUMEAQUAABAFJAD74EAAAQCUYB76CAAAAABL7YGBIBAL7YMAAAAD73H74IAFEHWITFNVQWS3BCHIRHIZLTORAGK6DBNVYGYZJOMNXW2IRMEJSW4ZBCHIRDEMBRHAWTCMBNGI3FIMJSHIYTSORTGMXDOMBZG43TIMJYHAVTAMR2GAYCE7IBGEBAPXB37ROJCUOYBVG4LAL3MSNKJKPGIKNT564PYK5X542NH62V7TAUEYHGLEOPZHRBAPH7M4SC55OHAEYQEXMKGG3JPO6BSHTDF3T5H6T42VUD7YAJ3TY5AP5MDE5QW4ZYWMSAPEK24HZOUXQ3LJ5YY34XYPVXBUAA====\"\n\n// the public key b32 encoded from the private key using: lkgen pub my_private_key_file`.\n// It should be hardcoded somewhere in your app.\nconst publicKeyBase32 = \"ARIVIK3FHZ72ERWX6FQ6Z3SIGHPSMCDBRCONFKQRWSDIUMEEESQULEKQ7J7MZVFZMJDFO6B46237GOZETQ4M2NE32C3UUNOV5EUVE3OIV72F5LQRZ6DFMM6UJPELARG7RLJWKQRATUWD5YT46Q2TKQMPPGIA====\"\n\n// Unmarshal the public key.\npublicKey, err := lk.PublicKeyFromB32String(publicKeyBase32)\nif err != nil {\n\tlog.Fatal(err)\n}\n\n// Unmarshal the customer license.\nlicense, err := lk.LicenseFromB32String(licenseB32)\nif err != nil {\n\tlog.Fatal(err)\n}\n\n// validate the license signature.\nif ok, err := license.Verify(publicKey); err != nil {\n\tlog.Fatal(err)\n} else if !ok {\n\tlog.Fatal(\"Invalid license signature\")\n}\n\nresult := struct {\n\tEmail string    `json:\"email\"`\n\tEnd   time.Time `json:\"end\"`\n}{}\n\n// unmarshal the document.\nif err := json.Unmarshal(license.Data, \u0026result); err != nil {\n\tlog.Fatal(err)\n}\n\n// Now you just have to check that the end date is after time.Now() then you can continue!\nif result.End.Before(time.Now()) {\n\tlog.Fatalf(\"License expired on: %s\", result.End.Format(\"2006-01-02\"))\n} else {\n\tfmt.Printf(`Licensed to %s until %s`, result.Email, result.End.Format(\"2006-01-02\"))\n}\n```\n\n\n#### A Complete example\n\nBellow is a sample function that generate a key pair, signs a license and verify it.\n\n```go\n// create a new Private key:\nprivateKey, err := lk.NewPrivateKey()\nif err != nil {\n\tlog.Fatal(err)\n\n}\n\n// create a license document:\ndoc := MyLicence{\n\t\"test@example.com\",\n\ttime.Now().Add(time.Hour * 24 * 365), // 1 year\n}\n\n// marshall the document to json bytes:\ndocBytes, err := json.Marshal(doc)\nif err != nil {\n\tlog.Fatal(err)\n\n}\n\n// generate your license with the private key and the document:\nlicense, err := lk.NewLicense(privateKey, docBytes)\nif err != nil {\n\tlog.Fatal(err)\n\n}\n\n// encode the new license to b64, this is what you give to your customer.\nstr64, err := license.ToB64String()\nif err != nil {\n\tlog.Fatal(err)\n\n}\nfmt.Println(str64)\n\n// get the public key. The public key should be hardcoded in your app to check licences.\n// Do not distribute the private key!\npublicKey := privateKey.GetPublicKey()\n\n// validate the license:\nif ok, err := license.Verify(publicKey); err != nil {\n\tlog.Fatal(err)\n} else if !ok {\n\tlog.Fatal(\"Invalid license signature\")\n}\n\n// unmarshal the document and check the end date:\nres := MyLicence{}\nif err := json.Unmarshal(license.Data, \u0026res); err != nil {\n\tlog.Fatal(err)\n} else if res.End.Before(time.Now()) {\n\tlog.Fatalf(\"License expired on: %s\", res.End.String())\n} else {\n\tfmt.Printf(`Licensed to %s until %s \\n`, res.Email, res.End.Format(\"2006-01-02\"))\n}\n```\n","funding_links":[],"categories":["Microsoft Office","杂项","Miscellaneous","Commercial Software Lisence Management","\u003cspan id=\"其他-miscellaneous\"\u003e其他 Miscellaneous\u003c/span\u003e","其他杂项","其他","Uncategorized","Go"],"sub_categories":["Uncategorized","未分类的","Advanced Console UIs","Strings","\u003cspan id=\"高级控制台用户界面-advanced-console-uis\"\u003e高级控制台用户界面 Advanced Console UIs\u003c/span\u003e","暂未分类这些库被放在这里是因为其他类别似乎都不适合。","暂未分类","交流"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhyperboloide%2Flk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhyperboloide%2Flk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhyperboloide%2Flk/lists"}