{"id":20514997,"url":"https://github.com/junkurihara/httpsig-rs","last_synced_at":"2025-08-21T02:31:12.434Z","repository":{"id":221350615,"uuid":"736641616","full_name":"junkurihara/httpsig-rs","owner":"junkurihara","description":"Rust implementation of IETF RFC 9421, http message signatures","archived":false,"fork":false,"pushed_at":"2025-02-20T00:58:12.000Z","size":209,"stargazers_count":23,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"develop","last_synced_at":"2025-08-14T17:57:32.626Z","etag":null,"topics":["http-message-signatures","http-signature","http-signatures","hyper","rfc9421","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","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/junkurihara.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","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},"funding":{"github":"junkurihara"}},"created_at":"2023-12-28T13:09:22.000Z","updated_at":"2025-05-19T10:34:45.000Z","dependencies_parsed_at":"2024-02-19T06:28:37.159Z","dependency_job_id":"48e128f8-cfa5-492f-b4f4-db5e79aef0b2","html_url":"https://github.com/junkurihara/httpsig-rs","commit_stats":null,"previous_names":["junkurihara/httpsig-rs"],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/junkurihara/httpsig-rs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/junkurihara%2Fhttpsig-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/junkurihara%2Fhttpsig-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/junkurihara%2Fhttpsig-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/junkurihara%2Fhttpsig-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/junkurihara","download_url":"https://codeload.github.com/junkurihara/httpsig-rs/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/junkurihara%2Fhttpsig-rs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271416767,"owners_count":24755949,"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-08-21T02:00:08.990Z","response_time":74,"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":["http-message-signatures","http-signature","http-signatures","hyper","rfc9421","rust"],"created_at":"2024-11-15T21:19:11.515Z","updated_at":"2025-08-21T02:31:12.205Z","avatar_url":"https://github.com/junkurihara.png","language":"Rust","readme":"# httpsig-rs\n\n\u003e **Work in Progress**\n\n[![httpsig](https://img.shields.io/crates/v/httpsig.svg)](https://crates.io/crates/httpsig)\n[![httpsig](https://docs.rs/httpsig/badge.svg)](https://docs.rs/httpsig)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n![Unit Test](https://github.com/junkurihara/httpsig-rs/actions/workflows/ci.yml/badge.svg)\n\nImplementation of [IETF RFC 9421](https://datatracker.ietf.org/doc/html/rfc9421) of http message signatures.\n\nThis crates provides a basic library [httpsig](./httpsig) and [its extension](./httpsig-hyper/) of [`hyper`](https://github.com/hyperium/hyper)'s http library. At this point, our library can sign and verify request and response messages of only `hyper`.\n\n## Supported Signature Algorithms\n\n- [x] HMAC using SHA-256\n- [x] Ed25519\n- [x] ECDSA-P256 using SHA-256\n- [ ] ECDSA-P384 using SHA-384\n\n~~- [ ] RSASSA-PSS using SHA-512~~\n\n~~- [ ] RSASSA-PKCS1-v1_5 using SHA-256~~\n\nAt this point, we have no plan to support RSA signature due to [the problem related to the non-constant time operation](https://github.com/RustCrypto/RSA/issues/19), i.e., [Mervin Attack](https://people.redhat.com/~hkario/marvin/).\n\n## Usage of Extension for `hyper` (`httpsig-hyper`)\n\nThis is a case signing and verifying a signature generated with asymmetric cryptography (like EdDSA), where `PUBLIC_KEY_STRING` and `SECRET_KEY_STRING` is a public and private keys in PEM format, respectively. Generating and verifying a MAC through symmetric crypto (HMAC-SHA256) is also supported.\n\n### Signing and Verifying a Request\n\n```rust\nuse http::Request;\nuse http_body_util::Full;\nuse httpsig_hyper::{prelude::*, *};\n\ntype SignatureName = String;\nconst COVERED_COMPONENTS: \u0026[\u0026str] = \u0026[\"@method\", \"date\", \"content-type\", \"content-digest\"];\n\n/// Signer function that generates a request with a signature\nasync fn signer\u003cB\u003e(\u0026mut req: Request\u003cB\u003e) -\u003e HttpSigResult\u003c()\u003e {\n  // build signature params that indicates objects to be signed\n  let covered_components = COVERED_COMPONENTS\n    .iter()\n    .map(|v| message_component::HttpMessageComponentId::try_from(*v))\n    .collect::\u003cResult\u003cVec\u003c_\u003e, _\u003e\u003e()\n    .unwrap();\n  let mut signature_params = HttpSignatureParams::try_new(\u0026covered_components).unwrap();\n\n  // set signing/verifying key information, alg and keyid\n  let secret_key = SecretKey::from_pem(SECRET_KEY_STRING).unwrap();\n  signature_params.set_key_info(\u0026secret_key);\n\n  req\n    .set_message_signature(\u0026signature_params, \u0026secret_key, Some(\"custom_sig_name\"))\n    .await\n}\n\n/// Validation function that verifies a request with a signature\nasync fn verifier\u003cB\u003e(req: \u0026Request\u003cB\u003e) -\u003e HttpSigResult\u003cSignatureName\u003e {\n  let public_key = PublicKey::from_pem(PUBLIC_KEY_STRING).unwrap();\n  let key_id = public_key.key_id();\n\n  // verify signature with checking key_id\n  req.verify_message_signature(\u0026public_key, Some(\u0026key_id)).await\n}\n\n#[tokio::main]\nasync fn main() {\n  let mut request_from_sender = ...;\n  let res = signer(request_from_sender).await;\n  assert!(res.is_ok())\n\n  // receiver verifies the request with a signature\n  let verified_message = receiver(\u0026request_from_sender).await;\n  assert!(verification_res.is_ok());\n\n  // if needed, content-digest can be verified separately\n  let verified_request = request_from_sender.verify_content_digest().await;\n  assert!(verified_request.is_ok());\n}\n\n```\n\n### Signing and Verifying a Response\n\n```rust\nuse http::{Request, Response};\nuse http_body_util::Full;\nuse httpsig_hyper::{prelude::*, *};\n\ntype SignatureName = String;\n\n/// This includes the method of the request corresponding to the request (the second element)\nconst COVERED_COMPONENTS: \u0026[\u0026str] = \u0026[\"@status\", \"\\\"@method\\\";req\", \"date\", \"content-type\", \"content-digest\"];\n\n/// Signer function that generates a response with a signature from response itself and corresponding request\nasync fn signer\u003cB\u003e(\u0026mut res: Response\u003cB\u003e, corresponding_req: \u0026Request\u003cB\u003e) -\u003e HttpSigResult\u003c()\u003e {\n  // build signature params that indicates objects to be signed\n  let covered_components = COVERED_COMPONENTS\n    .iter()\n    .map(|v| message_component::HttpMessageComponentId::try_from(*v))\n    .collect::\u003cResult\u003cVec\u003c_\u003e, _\u003e\u003e()\n    .unwrap();\n  let mut signature_params = HttpSignatureParams::try_new(\u0026covered_components).unwrap();\n\n  // set signing/verifying key information, alg and keyid\n  let secret_key = SecretKey::from_pem(SECRET_KEY_STRING).unwrap();\n  signature_params.set_key_info(\u0026secret_key);\n\n  req\n    .set_message_signature(\u0026signature_params, \u0026secret_key, Some(\"custom_sig_name\"), Some(corresponding_req))\n    .await\n}\n\n/// Validation function that verifies a response with a signature from response itself and sent request\nasync fn verifier\u003cB\u003e(res: \u0026Response\u003cB\u003e, sent_req: \u0026Request\u003cB\u003e) -\u003e HttpSigResult\u003cSignatureName\u003e {\n  let public_key = PublicKey::from_pem(PUBLIC_KEY_STRING).unwrap();\n  let key_id = public_key.key_id();\n\n  // verify signature with checking key_id\n  res.verify_message_signature(\u0026public_key, Some(\u0026key_id), Some(sent_req)).await\n}\n```\n\n## Examples\n\nSee [./httpsig-hyper/examples](./httpsig-hyper/examples/) for detailed examples with `hyper` extension.\n","funding_links":["https://github.com/sponsors/junkurihara"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjunkurihara%2Fhttpsig-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjunkurihara%2Fhttpsig-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjunkurihara%2Fhttpsig-rs/lists"}