{"id":29675777,"url":"https://github.com/oxidecomputer/sshauth","last_synced_at":"2025-07-22T23:38:42.387Z","repository":{"id":243122998,"uuid":"761516365","full_name":"oxidecomputer/sshauth","owner":"oxidecomputer","description":"A library for SSH key based (agents or static files) authentication tokens","archived":false,"fork":false,"pushed_at":"2024-10-17T21:45:45.000Z","size":74,"stargazers_count":7,"open_issues_count":0,"forks_count":1,"subscribers_count":23,"default_branch":"main","last_synced_at":"2025-06-30T16:50:02.692Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","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/oxidecomputer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APACHE","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-02-22T01:12:42.000Z","updated_at":"2025-04-04T04:33:16.000Z","dependencies_parsed_at":"2024-10-20T16:53:28.119Z","dependency_job_id":null,"html_url":"https://github.com/oxidecomputer/sshauth","commit_stats":null,"previous_names":["oxidecomputer/sshauth"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/oxidecomputer/sshauth","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxidecomputer%2Fsshauth","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxidecomputer%2Fsshauth/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxidecomputer%2Fsshauth/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxidecomputer%2Fsshauth/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/oxidecomputer","download_url":"https://codeload.github.com/oxidecomputer/sshauth/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxidecomputer%2Fsshauth/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266591232,"owners_count":23953082,"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-22T02:00:09.085Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"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":[],"created_at":"2025-07-22T23:38:34.736Z","updated_at":"2025-07-22T23:38:42.378Z","avatar_url":"https://github.com/oxidecomputer.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# sshauth: a library for SSH key based authentication tokens\n\nThe **sshauth** library produces and verifies ephemeral bearer tokens signed by\nan SSH private key, either directly or via the SSH agent.  These tokens are\nsuitable for authentication of a client to a server; e.g., using the HTTP\n**Authorization** header.\n\n## Example\n\nIn the client, choose an [SSH key](https://docs.rs/ssh-key/latest/ssh_key/) (an\n\"identity\" in SSH parlance), either by reading a key file or querying the agent\n(see [ssh-agent(1)](https://man.openbsd.org/ssh-agent.1) and\n[ssh-add(1)](https://man.openbsd.org/ssh-add.1) for more on key management):\n\n```rust\nlet authsock = env::var(\"SSH_AUTH_SOCK\")\n    .expect(\"SSH_AUTH_SOCK is unset or invalid\");\nlet public_key: ssh_key::public::PublicKey =\n    sshauth::agent::list_keys(\u0026authsock)\n    .await?\n    .into_iter()\n    .find(|key| {\n        /*\n         * Choose key of interest by type, fingerprint, comment, etc.\n         */\n        ...\n    })\n    .expect(\"can't find a suitable SSH identity\");\n```\n\nWith the key in hand, the client can generate, sign, and encode a token:\n\n```rust\nlet signer = sshauth::TokenSigner::using_authsock(authsock)?\n    .key(key)\n    .include_fingerprint(true)\n    .build()?;\nlet token: String = signer\n    .sign_for()\n    .sign()\n    .await?\n    .encode();\n```\n\nThen, assuming that the public key has been previously registered and stored\non the server (e.g., in a database or file system), authentication code in\nthe server can look at the fingerprint in the unverified token, fetch the\ncorresponding public key, and verify the token's signature:\n\n```rust\n/*\n * Get the token; e.g., from the \"Authorization\" header of a HTTP request:\n */\nlet raw_token: String = \"...\";\nlet unverified_token = sshauth::UnverifiedToken::try_from(raw_token.as_str())?;\nlet fingerprint = unverified_token\n    .untrusted_fingerprint()\n    .expect(\"token must include fingerprint\");\n/*\n * Fetch the registered public key matching the fingerprint and verify the\n * signature:\n */\nlet public_key = your_database.key_for_fingerprint(\u0026fingerprint)?;\nlet verified_token = unverified_token.verify_for().with_key(\u0026public_key)?;\n```\n\n## Clock Requirements\n\nIn order to limit the useful lifetime of a token, the current time of day\nis included in the signed blob so that it can be verified on the server.\nBy default, the client and the server clock must agree on time to within\na delta of 60 seconds.  It is recommended that both clients and servers\nbe configured with NTP to avoid issues.\n\nIf your system provides looser guarantees about time, or you want to be able to\nreuse tokens for a longer period, you can relax this requirement -- with the\nconcomitant reduction in security in the face of leaked or intercepted tokens.\nUse `.max_skew_seconds(seconds)` with a wider range when verifying a token.\nThere is presently no way to completely disable time stamp generation or\nvalidation.\n\nNote that while the client is signing a time stamp, that does not mean that the\nclient produced the signature at the actual nominated time.  Client clocks are\nunder client control, and could be wound or backwards arbitrarily while\nproducing signatures.  If you need a verifiable time stamp, you would need some\nother mechanism to achieve that; e.g., a quorum of independent time stamp\nsigning servers, which could _themselves_ use SSH tokens for authentication.\n\n## Actions\n\nTo reduce the scope of replay attacks the signed blob can include an arbitrary\nlist of actions, described as string key-value pairs.  Applications are\nexpected to include in the action list anything that they would like to verify\nabout the request to which the token is attached; e.g., the HTTP method,\nauthority, query string, and the digest of a POST body would make it more\ndifficult for an attacker to re-use an intercepted token to make different\nrequests.\n\nTo keep token size manageable, the action list is not included in the token\nitself.  The token signer and the token verifier must agree in advance on the\norder and precise contents of the action list.  Any mismatch will lead to a\nverification failure.\n\n## Magic Prefix\n\nTo make cross-protocol attacks more difficult, the library supports a\nfixed-length prefix for the signed blob that the consuming system using the\n`.magic_prefix([u8; 8])` routine on signers and verifiers.   Two systems using\ndifferent magic prefixes will produce signatures that are mutually\nunverifiable.  The client and the server must agree, at the level of their\nmutually agreed application protocol, on a constant prefix value.\n\n## Identity\n\nAs an alternative to using fingerprints to identify keys, the library also\nsupports using (restricted) file names; this can be handy if you're using a\nfile system as your key store, or if you want to look up keys by a synthetic\nidentifier like a login name.  Just replace `.include_fingerprint(true)` with\n`.identity_filename(path)` on the client side, and instead of\n`.untrusted_fingerprint()` on the server, use `.untrusted_identity_filename()`.\n\nIt is also possible to combine the use of an identity file name and an included\nfingerprint within the same token if desired.\n\n## Public Key Sources\n\nThe library provides two basic helper modules for getting started with a public\nkey source in small applications that operate chiefly out of the local file\nsystem:\n\n* The `keyfile::parse_authorized_keys()` routine will parse an OpenSSH\n  `authorized_keys` file and return a list of public keys from that file.\n* The `keydir::KeyDirectory` object builds on this to perform a lookup in\n  a nominated directory, using the _identity file name_ that may be provided\n  in a token.  That file name will be used to load a file from the key\n  directory, and the token is considered valid if any of the public keys\n  in that file allow a successful verification.\n\nThe system presently supports the following key types:\n\n- ECDSA keys using the NIST P-256 curve\n- Ed25519 keys\n\n## Tuneables\n\nThere are many other tunable token parameters (expiration, signing strategy,\netc.), but the defaults should be reasonable for many applications.\n\nThe library is intended to be difficult to misuse.  If you find a way of\n\"holding it wrong\" (e.g., trusting data before verification, constructing\nunsafe or unsigned tokens, skipping or improperly verifying signatures, etc.),\nplease [let us know](#contributions).\n\n## Limitations\n\nThis library is limited in scope to public key authentication.  It is not\nintended to be a general-purpose identity provider or authorization system.\nThere is no web component or integration with any third-party services.  It\ndoes not directly support any kind of challenge/response protocol (although one\ncan be layered on top through the use of **action** key-value pairs), and so in\nprinciple is vulnerable to certain kinds of replay attacks.  If you need any of\nthose features, you might be better served by other protocols such as SAML,\nOAuth, OIDC, etc.\n\nIf this library is able to verify a token, you may assume that the producer\nof that token had access to the corresponding private key at the time of\ngeneration, which was within the allowed window.  In the absence of other\ninformation, you may not generally assume anything else about the signing\nsystem, user, or owner of that private key.\n\n## Contributions\n\nHelpful contributions of any kind are welcome; please submit\nan [issue](https://github.com/oxidecomputer/sshauth/issues)\nor open a [pull request](https://github.com/oxidecomputer/sshauth/pulls)\non our [GitHub repository](https://github.com/oxidecomputer/sshauth).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foxidecomputer%2Fsshauth","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foxidecomputer%2Fsshauth","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foxidecomputer%2Fsshauth/lists"}