{"id":29445522,"url":"https://github.com/usherlabs/ecdsa-jwt","last_synced_at":"2026-01-20T17:02:52.039Z","repository":{"id":298267440,"uuid":"999287881","full_name":"usherlabs/ecdsa-jwt","owner":"usherlabs","description":"ECDSA challenge-based authentication with JWT session management","archived":false,"fork":false,"pushed_at":"2025-07-05T05:21:24.000Z","size":146,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-26T20:23:58.670Z","etag":null,"topics":["authentication","cryptography","ecdsa","jwt","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/usherlabs.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,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-06-10T03:11:47.000Z","updated_at":"2025-07-05T05:21:07.000Z","dependencies_parsed_at":"2025-06-27T13:26:39.588Z","dependency_job_id":"d1db6de6-626b-4de2-b901-f68fad2abc1a","html_url":"https://github.com/usherlabs/ecdsa-jwt","commit_stats":null,"previous_names":["usherlabs/ecdsa-jwt"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/usherlabs/ecdsa-jwt","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/usherlabs%2Fecdsa-jwt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/usherlabs%2Fecdsa-jwt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/usherlabs%2Fecdsa-jwt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/usherlabs%2Fecdsa-jwt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/usherlabs","download_url":"https://codeload.github.com/usherlabs/ecdsa-jwt/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/usherlabs%2Fecdsa-jwt/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28607624,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-20T16:10:39.856Z","status":"ssl_error","status_checked_at":"2026-01-20T16:10:39.493Z","response_time":117,"last_error":"SSL_read: 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":["authentication","cryptography","ecdsa","jwt","rust"],"created_at":"2025-07-13T17:13:38.104Z","updated_at":"2026-01-20T17:02:52.024Z","avatar_url":"https://github.com/usherlabs.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ecdsa-jwt\n\nA Rust library for **ECDSA challenge-based authentication** with **flexible JWT session management**. Provides server-side cryptographic operations for secure, passwordless authentication where clients prove ownership of ECDSA private keys by signing challenges.\n\n## Features\n\n- **ECDSA Signature Verification** - Verify signatures using secp256k1 curve\n- **Secure Challenge Generation** - Cryptographically secure 32-byte challenges\n- **Flexible JWT Session Management** - Create and validate JWTs with or without embedded public keys\n- **Stateless Design** - No built-in storage, you control data persistence\n- **Comprehensive Error Handling** - Detailed error types for debugging\n\n## Quick Start\n\nAdd to your `Cargo.toml`:\n\n```toml\n[dependencies]\necdsa-jwt = \"0.1\"\n```\n\n## Usage\n\n### Authentication Flow\n\nSee the authentication flow in the [basic_workflow example](examples/basic_workflow.rs):\n\n```bash\ncargo run --example basic_workflow\n```\n\nThe example demonstrates:\n\n- Challenge generation and storage\n- Authentication with signed challenges\n- JWT creation with optional public key inclusion\n- JWT validation and session management\n\n\n\n### Generating ECDSA Keys\n\n#### Using OpenSSL (Recommended)\n\n```bash\n# Generate private key\nopenssl ecparam -genkey -name secp256k1 -out private_key.pem\nopenssl ec -in private_key.pem -out private_key.pem\n\n# Extract public key\nopenssl ec -in private_key.pem -pubout -out public_key.pem\n\n# View the keys\ncat private_key.pem\ncat public_key.pem\n\n# Generate SHA256 hash of public key\ncat public_key.pem | openssl dgst -sha256\n```\n\n#### Using Rust (p256 crate)\n\n```rust\nuse p256::ecdsa::{SigningKey, VerifyingKey};\nuse p256::SecretKey;\nuse rand::rngs::OsRng;\nuse base64::prelude::*;\n\n// Generate key pair\nlet private_key = SecretKey::random(\u0026mut OsRng);\nlet signing_key = SigningKey::from(private_key);\nlet verifying_key = VerifyingKey::from(\u0026signing_key);\n\n// Export public key as PEM\nlet public_key_pem = verifying_key.to_encoded_point(false).to_string();\nlet public_key_pem = format!(\n    \"-----BEGIN PUBLIC KEY-----\\n{}\\n-----END PUBLIC KEY-----\",\n    base64::engine::general_purpose::STANDARD.encode(public_key_pem.as_bytes())\n);\n```\n\n#### JWT Structure Examples\n\n**With public key (for signature verification):**\n```json\n{\n  \"sub\": \"550e8400-e29b-41d4-a716-446655440000\",\n  \"exp\": 1640995200,\n  \"iat\": 1640991600,\n  \"key_hash\": \"a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef12345678\",\n  \"public_key\": \"-----BEGIN PUBLIC KEY-----\\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...\\n-----END PUBLIC KEY-----\"\n}\n```\n\n**Without public key (smaller, for simple authentication):**\n```json\n{\n  \"sub\": \"550e8400-e29b-41d4-a716-446655440000\",\n  \"exp\": 1640995200,\n  \"iat\": 1640991600,\n  \"key_hash\": null,\n  \"public_key\": null\n}\n```\n\n#### Creating JWTs Manually\n\n```rust\nuse ecdsa_jwt::crypto::jwt::create_jwt;\nuse uuid::Uuid;\n\n// Create JWT without public key (smaller size)\nlet token = create_jwt(session_id, None, \u0026config)?;\n\n// Create JWT with public key\nlet public_key = Some(\"-----BEGIN PUBLIC KEY-----\\n...\\n-----END PUBLIC KEY-----\".to_string());\nlet token = create_jwt(session_id, public_key, \u0026config)?;\n```\n\n### Client-Side Signing Example\n\n```rust\nuse p256::ecdsa::{SigningKey, VerifyingKey};\nuse p256::SecretKey;\nuse rand::rngs::OsRng;\nuse base64::prelude::*;\n\n// Load private key from PEM file\nlet private_key_pem = std::fs::read_to_string(\"private_key.pem\").unwrap();\nlet private_key = SecretKey::from_sec1_pem(\u0026private_key_pem).unwrap();\nlet signing_key = SigningKey::from(private_key);\n\n// Load public key from PEM file  \nlet public_key = std::fs::read_to_string(\"public_key.pem\").unwrap();\n\n// Sign a challenge\nlet challenge = \"base64-encoded-challenge-from-server\";\nlet challenge_bytes = base64::decode(challenge).unwrap();\nlet signature = signing_key.sign(\u0026challenge_bytes);\nlet signature_b64 = base64::encode(signature.to_bytes());\n\n// Send to server: challenge, signature_b64, and public_key\n```\n\n### Individual Functions\n\n```rust\nuse ecdsa_jwt::{generate_challenge, verify_signature, create_jwt, validate_token};\n\n// Generate challenge\nlet challenge = generate_challenge();\n\n// Verify signature  \nlet challenge_bytes = base64::decode(\u0026challenge)?;\nlet signature_bytes = base64::decode(\u0026signature)?;\nverify_signature(\u0026public_key, \u0026challenge_bytes, \u0026signature_bytes)?;\n\n// Create JWT (with optional public key)\nlet token = create_jwt(session_id, Some(public_key), \u0026jwt_config)?;\n\n// Validate JWT\nlet claims = validate_token(\u0026token, \u0026jwt_config)?;\n\n// Verify signature using public key from JWT (requires JWT with embedded public key)\nuse ecdsa_jwt::AuthService;\nlet auth_service = AuthService::new(jwt_config);\nmatch auth_service.verify_signature_from_jwt(\n    \u0026token,\n    challenge.as_bytes(),\n    \u0026signature_bytes\n) {\n    Ok(()) =\u003e println!(\"Signature verified using JWT public key!\"),\n    Err(e) =\u003e println!(\"Verification failed: {}\", e),\n}\n```\n\n## API Reference\n\n### AuthService\n\n```rust\nimpl AuthService {\n    pub fn new(jwt_config: JwtConfig) -\u003e Self;\n    pub fn generate_challenge(\u0026self) -\u003e String;\n    pub fn authenticate(\u0026self, request: AuthRequest, include_public_key: bool) -\u003e Result\u003cAuthResponse\u003e;\n    pub fn validate_session(\u0026self, token: \u0026str) -\u003e Result\u003cClaims\u003e;\n    pub fn verify_signature_from_jwt(\u0026self, jwt_token: \u0026str, challenge: \u0026[u8], signature: \u0026[u8]) -\u003e Result\u003c()\u003e;\n}\n```\n\n### Core Types\n\n```rust\npub struct AuthRequest {\n    pub challenge: String,        // Base64-encoded challenge\n    pub signature: String,        // Base64-encoded signature\n    pub public_key: String,       // Public key\n}\n\npub struct AuthResponse {\n    pub session_id: Uuid,        // Session identifier\n    pub session_token: String,   // JWT token\n    pub expires_at: i64,         // Unix timestamp\n}\n\npub struct JwtConfig {\n    pub secret: Secret\u003cString\u003e,  // Base64-encoded secret\n    pub ttl: i64,               // Token lifetime (seconds)\n}\n\npub struct Claims {\n    pub sub: Uuid,              // Session identifier\n    pub exp: i64,              // Expiration timestamp\n    pub iat: i64,              // Issued at timestamp\n    pub key_hash: Option\u003cString\u003e,      // SHA256 hash of public key (optional)\n    pub public_key: Option\u003cString\u003e, // Full public key PEM (optional)\n}\n```\n\n### Utility Functions\n\n```rust\n// Challenge operations\npub fn generate_challenge() -\u003e String;\npub fn decode_challenge(challenge_b64: \u0026str) -\u003e Result\u003cVec\u003cu8\u003e\u003e;\n\n// Signature verification\npub fn verify_signature(public_key: \u0026str, challenge: \u0026[u8], signature: \u0026[u8]) -\u003e Result\u003c()\u003e;\npub fn verify_signature_b64(public_key: \u0026str, challenge_b64: \u0026str, signature_b64: \u0026str) -\u003e Result\u003c()\u003e;\npub fn verify_signature_eth(public_key: \u0026str, challenge_b64: \u0026str, signature_b64: \u0026str) -\u003e Result\u003c()\u003e;\npub fn verify_signature_pem(public_key: \u0026str, challenge_b64: \u0026str, signature_b64: \u0026str) -\u003e Result\u003c()\u003e;\n\n// JWT operations (public key is optional)\npub fn create_jwt(session_id: Uuid, public_key: Option\u003cString\u003e, config: \u0026JwtConfig) -\u003e Result\u003cString\u003e;\npub fn validate_token(token: \u0026str, config: \u0026JwtConfig) -\u003e Result\u003cClaims\u003e;\npub fn verify_signature_from_jwt(token: \u0026str, config: \u0026JwtConfig, challenge: \u0026[u8], signature: \u0026[u8]) -\u003e Result\u003c()\u003e;\n```\n\n## Security Considerations\n\n### JWT Security\n\n- **HMAC-SHA256 signing** - Prevents tampering\n- **Expiration timestamps** - Automatic token expiry\n- **Cryptographic verification** - Cannot be forged without the secret\n- **Stateless design** - No server-side session storage required\n\n### Best Practices\n\n- **Use strong secrets** (at least 256 bits) for JWT signing\n- **Set appropriate TTL** (1 hour recommended for most use cases)\n- **Always use HTTPS** in production\n- **Store challenges securely** with short expiration (5-15 minutes)\n- **Remove challenges after use** to prevent replay attacks\n- **Validate tokens on every request** to protected resources\n\n### Public Key Management\n\n- **Include public key in JWT** when you need signature verification\n- **Use key hash only** for smaller JWTs when full key isn't needed\n- **Store key mappings** in database when using hash-only approach\n- **Rotate keys regularly** for enhanced security\n\n## Testing\n\n```bash\ncargo test\n```\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fusherlabs%2Fecdsa-jwt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fusherlabs%2Fecdsa-jwt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fusherlabs%2Fecdsa-jwt/lists"}