{"id":43045411,"url":"https://github.com/as/signer","last_synced_at":"2026-01-31T09:46:28.211Z","repository":{"id":57529433,"uuid":"259213870","full_name":"as/signer","owner":"as","description":"base64-encoded token system utilizing xchacha20poly1305 (see also: branca)","archived":false,"fork":false,"pushed_at":"2021-10-07T08:39:29.000Z","size":1155,"stargazers_count":4,"open_issues_count":0,"forks_count":2,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-06-20T05:21:31.887Z","etag":null,"topics":["base64","branca","chacha20poly1305","jwt","token"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/as.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":"2020-04-27T05:25:03.000Z","updated_at":"2023-01-19T22:41:59.000Z","dependencies_parsed_at":"2022-08-26T05:02:35.517Z","dependency_job_id":null,"html_url":"https://github.com/as/signer","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/as/signer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/as%2Fsigner","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/as%2Fsigner/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/as%2Fsigner/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/as%2Fsigner/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/as","download_url":"https://codeload.github.com/as/signer/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/as%2Fsigner/sbom","scorecard":{"id":210596,"data":{"date":"2025-08-11","repo":{"name":"github.com/as/signer","commit":"84c5318abe88c358b4e2326971a1b3e6f36e4c2f"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.1,"checks":[{"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":"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":"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":"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":"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":"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":"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":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: BSD 3-Clause \"New\" or \"Revised\" 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":"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":"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":"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":"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 1 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":3,"reason":"7 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GO-2021-0227 / GHSA-3vm4-22fp-5rfm","Warn: Project is vulnerable to: GO-2022-0968 / GHSA-gwc9-m7rh-j2ww","Warn: Project is vulnerable to: GO-2021-0356 / GHSA-8c26-wmh5-6g9v","Warn: Project is vulnerable to: GO-2024-2961","Warn: Project is vulnerable to: GO-2023-2402 / GHSA-45x7-px36-x8w8","Warn: Project is vulnerable to: GO-2024-3321 / GHSA-v778-237x-gjrc","Warn: Project is vulnerable to: GO-2025-3487 / GHSA-hcg3-q754-cr77"],"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-17T00:42:43.107Z","repository_id":57529433,"created_at":"2025-08-17T00:42:43.107Z","updated_at":"2025-08-17T00:42:43.107Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28937572,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-31T08:53:31.997Z","status":"ssl_error","status_checked_at":"2026-01-31T08:51:38.521Z","response_time":128,"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":["base64","branca","chacha20poly1305","jwt","token"],"created_at":"2026-01-31T09:46:27.311Z","updated_at":"2026-01-31T09:46:28.204Z","avatar_url":"https://github.com/as.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Signer\n\nSigner is a simple token generation scheme using `xchacha20poly1305`. It generates an authenticated\nand encrypted token which can be decrypted and verified by the same key. Signer is basically \"Branca\"\nwithout the crufty base32 and 32-bit binary time field. It uses url-safe base64 encoding.\n\nThe Token type is a byte slice implementing base64 MarshalText and UnmarshalText.\n\n# Use Case\n\nYou have a server that wants to give clients a token. The server need to be able to verify\nthat the token it issued came from the server (via the same key) and that this token was\nnot modified by the client or some other party. The server also wants to keep the information\ninside the token private, and only accessible by the server or other parties in possession of\nthe key. The authentication is symmetric. Only servers in possesion of the key can verify the\ntoken's authenticity.\n\nYou should not use Signer if the client (not just the server) needs to read and/or authenticate the token\nE.G., asymmetric authentication with RSA or an elliptic curve. This token is for servers that issue\ntokens, perhaps for sessions or other data.\n\nFor example: If you are using JWT with HMAC (and/or encryption), you can replace it with signer. \nIf you are using JWT with RSA/EC, you can not.\n\n# Wire Format\n\nBelow is the Token's wire format:\n```\n\tversion[1] nonce[24] ciphertext[...] tag[16] | base64\n\n\tThe first 1+24 bytes are the header, authenticated by the AEAD, but not encrypted.\n\tThe version is fixed to 0x41 (A)\n\tThe nonce is a randomly-generated 24-byte string\n\n\tThe rest is the output of the AEAD, the ciphertext and 16 byte tag.\n\tThe ciphertext is the encrypted msg.\n\tThe tag is a message authentication code (MAC) used to verify the integrity of the header and ciphertext\n\n\tFinally, the vertical bar denotes the Token's intended string encoding is base64 (url-safe)\n```\n\n# Interface (caller defined)\n```go\ntype Signer interface{\n\t// Sign creates a token using the msg and nonce, if nonce is nil\n\t// one is generated automatically using a CSPRNG (crypto/rand.Read)\n\tSign(msg []byte, nonce []byte) (Token, error)\n\t\n\t// Verify authenticates the token and returns the decrypted msg\n\tVerify(t Token) (msg []byte, err error)\n}\n```\n\n# Usage Snippet\n```go\n\t// Configure\n\tkey := [32]byte{ /* random data */ }\n\ts, _ := signer.New(key[:])\n\n\t// Sign\n\ttok, _ := s.Sign([]byte(\"hello world\"), nil)\n\tfmt.Println(tok)\n\t// ul6mbjrzW_Y82_a8sQQRqlzFTPAcA65tn4xlWN3z3bpwIYZiW47JlyF34UwaUzize4yFfrN8Vzs\n\n\t// Verify\n\tp, err := s.Verify(tok)\n\tif err != nil{\n\t\tlog.Fatalf(\"verify: %v\", err)\n\t}\n```\n\n# Notes\n\n## Why not use branca?\nBranca's uint32 time field isn't future-proof and ignorant of time predating 1970 (time is a signed value). Time in a token specification is scope creep. Add your own time in the msg to excersize full control, since its guaranteed to be authenitc.  \n\nBranca implementations seen in the wild don't return msg if it is authentic but expired, which is useless for practical deployments. With Signer, you have the ability to log authentic but expired tokens to debug misbehaving clients or bugs in clients software.\n\nBranca uses a binary time field, one advantage of having a time field would be visibility in your server logs. For example, if logs contained a unix timestamp as part of the branca header, you could easily see in Splunk whether a certain timestamp was expired. However, Branca uses a binary timestamp, so the benefit of having a greppable/searchable value is lost. You must decode the timestamp manually and in the correct big endian byte order before making assumptions about it even if you assume its authentic.\n\nBranca uses base62, a poorly-defined standard (branca test vectors could not be decoded by online base62 decoders). A standard base64 encoding is easily accessible across languages and easier to implement and test.\n\nBranca does not offer easily-available test vectors. Java implementations in the wild incorrectly implement AEADs that only authenticate the header and not the cipher text. We provide an interface that allows the user to pass in the nonce because in practice this is CRITICAL to reproducibly verifying test vectors in the implementation.\n\n## Why not use JWT?\n\nJWT is vulnerable to downgrade attacks, because it supports \"none\" as an encryption algorithm. Signer supports only one algorithm, so a downgrade attack is impossible by design. If chacha20poly1305 is broken in the distant future, you can use another type of token. It is not wise to rely on dynamic implementations based on token versions. Just tell the server what to expect.\n\nJWT spec is complex and bloated. Signer is a bare-bones token that provides authentication and encryption only. It assumes the user can implement their own claims, authorization (not to be confused with authentication), and timeouts using the data inside the payload itself.\n\n## Why not use Signer?\n\nYou want the client to be able to validate the contents of the token, using the servers public key. Signer does not support this usecase at the time of writing, and would require asymmetric public key encryption (which is an order of magnitude slower).\n\nYou don't want the contents of the token to be encrypted. It is typical to conflate encryption and authentication, but the two are not always necessary together. Signer both authenticates and encrypts the input. \n\nYou completely distrust the programmers. This implementation allows the user to pass in a nonce, which should be random if you want identical inputs to generate distinct outputs. Many \"idiot-proof\" crypto packages hide the nonce-generation away from the API. However, this makes testing a pain, and sometimes the use-case calls for a deterministic nonce.\n\n# Base64 Encoding\n\nThe Token type uses URL-safe base64-encoding without padding characters. Below is the well-known character-set that comprises base64 url-safe encoding:\n\n`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_`\n\n# Test Vectors\n\nThis is the output of the zero vector, composed of a 32-byte key and nonce of all zero bits in binary and base64 url-safe format for your implementation in other languages:\n\n\t\tname:  \"zero\",\n\t\tinput: \"\",\n\t\tkey:   [32]byte{},\n\t\tnonce: [24]byte{},\n\t\tbinary: \"A\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\b\\xac\\x81ƕ\\xb5;\\xefw\\n\\xde5PU\\xde\",\n\t\ttext:   \"QQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAisgcaVtc2-73cK3jVQVd4\",\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fas%2Fsigner","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fas%2Fsigner","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fas%2Fsigner/lists"}