{"id":27063730,"url":"https://github.com/abdelstark/nostringer-rs","last_synced_at":"2025-04-05T16:20:52.304Z","repository":{"id":284905160,"uuid":"956060373","full_name":"AbdelStark/nostringer-rs","owner":"AbdelStark","description":"Ring signatures (SAG, BLSAG) for Nostr, written in Rust 🦀","archived":false,"fork":false,"pushed_at":"2025-04-03T14:26:51.000Z","size":286,"stargazers_count":7,"open_issues_count":3,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-04T23:46:47.149Z","etag":null,"topics":["monero","nostr","privacy","ring-signatures","secp256k1"],"latest_commit_sha":null,"homepage":"https://docs.rs/nostringer/latest/nostringer/","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/AbdelStark.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}},"created_at":"2025-03-27T16:25:35.000Z","updated_at":"2025-04-03T15:22:43.000Z","dependencies_parsed_at":"2025-03-28T10:03:15.267Z","dependency_job_id":"d362c345-5ddd-40ae-986b-896c5ee46648","html_url":"https://github.com/AbdelStark/nostringer-rs","commit_stats":null,"previous_names":["abdelstark/nostringer-rs"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AbdelStark%2Fnostringer-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AbdelStark%2Fnostringer-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AbdelStark%2Fnostringer-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AbdelStark%2Fnostringer-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AbdelStark","download_url":"https://codeload.github.com/AbdelStark/nostringer-rs/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247362575,"owners_count":20926801,"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","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":["monero","nostr","privacy","ring-signatures","secp256k1"],"created_at":"2025-04-05T16:20:51.611Z","updated_at":"2025-04-05T16:20:52.296Z","avatar_url":"https://github.com/AbdelStark.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n\u003ca href=\"https://github.com/AbdelStark/nostringer-rs/actions/workflows/rust.yml\"\u003e\u003cimg alt=\"GitHub Workflow Status\" src=\"https://img.shields.io/github/actions/workflow/status/AbdelStark/nostringer-rs/rust.yml?style=for-the-badge\u0026label=CI\" height=30\u003e\u003c/a\u003e\n\u003ca href=\"https://crates.io/crates/nostringer\"\u003e\u003cimg alt=\"Crates.io\" src=\"https://img.shields.io/crates/v/nostringer.svg?style=for-the-badge\u0026label=crates.io\" height=30\u003e\u003c/a\u003e\n\u003ca href=\"https://docs.rs/nostringer\"\u003e\u003cimg alt=\"Docs.rs\" src=\"https://docs.rs/nostringer/badge.svg?style=for-the-badge\u0026label=docs.rs\" height=30\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/AbdelStark/nostringer-rs/blob/main/LICENSE\"\u003e\u003cimg alt=\"License: MIT\" src=\"https://img.shields.io/badge/License-MIT-yellow.svg?style=for-the-badge\" height=30\u003e\u003c/a\u003e\n\u003ca href=\"https://bitcoin.org/\"\u003e \u003cimg alt=\"Bitcoin\" src=\"https://img.shields.io/badge/Bitcoin-FF9900?style=for-the-badge\u0026logo=bitcoin\u0026logoColor=white\" height=30\u003e\u003c/a\u003e\n\u003ca href=\"https://www.getmonero.org/\"\u003e \u003cimg alt=\"Monero\" src=\"https://img.shields.io/badge/Monero-000?style=for-the-badge\u0026logo=monero\u0026logoColor=white\" height=30\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/nostr-protocol/nostr\"\u003e \u003cimg alt=\"Nostr\" src=\"https://img.shields.io/badge/Nostr-8E44AD?style=for-the-badge\" height=30\u003e\u003c/a\u003e\n\n\u003c/div\u003e\n\n# Nostringer Ring Signatures (Rust)\n\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/AbdelStark/nostringer/main/assets/img/nostringer.png\" alt=\"Nostringer Logo\" width=\"250\"\u003e\n\n  \u003ch3\u003e\n   \u003ca href=\"https://github.com/AbdelStark/nostringer-rs/ROADMAP.md\"\u003e\n      ROADMAP\n    \u003c/a\u003e\n   \u003cspan\u003e | \u003c/span\u003e\n    \u003ca href=\"https://nostringer.starknetonbitcoin.com/\"\u003e\n      LIVE DEMO\n    \u003c/a\u003e\n    \u003cspan\u003e | \u003c/span\u003e\n    \u003ca href=\"https://docs.rs/nostringer/latest/nostringer/\"\u003e\n      RUST DOC\n    \u003c/a\u003e\n    \u003cspan\u003e | \u003c/span\u003e\n    \u003ca href=\"https://github.com/AbdelStark/nostringer-rs/tree/main/crates/nostringer/examples\"\u003e\n      EXAMPLES\n    \u003c/a\u003e\n  \u003c/h3\u003e\n\u003c/div\u003e\n\nA **blazing fast** Rust implementation of the Nostringer **unlinkable ring signature** scheme for Nostr, compatible with the [nostringer](https://github.com/AbdelStark/nostringer) TypeScript library.\n\nBuilt using pure Rust crypto crates, this library allows a signer to prove membership in a group of Nostr accounts (defined by their public keys) without revealing which specific account produced the signature. It uses a Spontaneous Anonymous Group (SAG)-like algorithm compatible with secp256k1 keys used in Nostr.\n\nNostringer is largely inspired by [Monero's Ring Signatures](https://www.getmonero.org/library/Zero-to-Monero-2-0-0.pdf) using Spontaneous Anonymous Group signatures (SAG), and [beritani/ring-signatures](https://github.com/beritani/ring-signatures) implementation of ring signatures using the elliptic curve Ed25519 and Keccak for hashing.\n\n## Table of Contents\n\n- [Nostringer Ring Signatures (Rust)](#nostringer-ring-signatures-rust)\n  - [Table of Contents](#table-of-contents)\n  - [Problem Statement](#problem-statement)\n  - [Roadmap](#roadmap)\n  - [Key Features](#key-features)\n  - [Signature Variants](#signature-variants)\n    - [SAG (Spontaneous Anonymous Group)](#sag-spontaneous-anonymous-group)\n    - [BLSAG (Back's Linkable Spontaneous Anonymous Group)](#blsag-backs-linkable-spontaneous-anonymous-group)\n  - [SAG vs. bLSAG Trade-offs](#sag-vs-blsag-trade-offs)\n  - [Installation](#installation)\n  - [Usage](#usage)\n    - [Using Different Signature Variants](#using-different-signature-variants)\n    - [Low-Level Binary API](#low-level-binary-api)\n    - [WebAssembly Usage](#webassembly-usage)\n      - [Building for WASM](#building-for-wasm)\n  - [Examples](#examples)\n  - [Benchmarks](#benchmarks)\n    - [Performance Results](#performance-results)\n  - [API Reference](#api-reference)\n  - [Signature Size](#signature-size)\n  - [Security Considerations](#security-considerations)\n  - [Disclaimer](#disclaimer)\n  - [License](#license)\n  - [References](#references)\n\n## Problem Statement\n\nIn many scenarios, you want to prove that \"someone among these N credentials produced this signature,\" but you do **not** want to reveal _which_ credential or identity. For instance, you might have a set of recognized Nostr pubkeys (e.g., moderators, DAO members, authorized reviewers) who are allowed to perform certain actions, but you want them to remain anonymous within that set when doing so.\n\nA **ring signature** solves this by letting an individual sign a message _on behalf of the group_ (the ring). A verifier can confirm the message originated from **one** of the public keys in the ring, without learning the specific signer's identity.\n\n## Roadmap\n\nCheck [ROADMAP.md](ROADMAP.md) for the detailed project roadmap, including completed and upcoming milestones.\n\n## Key Features\n\n- **Simplified API**: Top-level `sign` and `verify` functions with compact format signatures for easier use.\n- **Variant Selection**: Choose between SAG (unlinkable) and BLSAG (linkable) signature variants with a simple enum.\n- **Compact Signatures**: All signatures use the space-efficient \"ringA...\" format, which includes version and variant information.\n- **Unlinkable**: SAG signatures hide the signer's identity. Two signatures from the same signer cannot be linked cryptographically.\n- **Linkable Option**: The BLSAG variant provides linkability through key images to detect when the same key is used multiple times, while still preserving anonymity within the ring.\n- **Fast**: Implemented in Rust, leveraging efficient and audited cryptographic primitives from the RustCrypto ecosystem (`k256`, `sha2`).\n- **Optimized API**: Provides both high-level API and a more efficient low-level binary API that avoids serialization overhead.\n- **WebAssembly Support**: Use the library directly in web browsers and other WASM environments.\n- **Nostr Key Compatibility**: Directly supports standard Nostr key formats (hex strings):\n  - 32-byte (64-hex) x-only public keys.\n  - 33-byte (66-hex) compressed public keys.\n  - 65-byte (130-hex) uncompressed public keys.\n  - 32-byte (64-hex) private keys.\n- **Minimal Dependencies**: Relies on well-maintained RustCrypto crates.\n- **No Trusted Setup**: The scheme does not require any special setup ceremony.\n\n## Signature Variants\n\nThe library offers two main variants of ring signatures:\n\n### SAG (Spontaneous Anonymous Group)\n\nThe default variant that provides:\n\n- Complete unlinkability (no way to tell if two signatures came from the same signer)\n- Maximum privacy within the ring\n- Suitable for anonymous voting, whistleblowing, or any scenario requiring maximum privacy\n\n### BLSAG (Back's Linkable Spontaneous Anonymous Group)\n\nA linkable variant that:\n\n- Produces a key image along with the signature to enable linkability\n- Can detect when the same key signs multiple times (via the key image)\n- Still doesn't reveal which specific ring member signed (preserves anonymity within the ring)\n- Suitable for preventing double-spending, duplicate voting, or tracking usage of a credential\n- Similar to the linkable ring signature scheme used in Monero\n\nChoose the variant that best suits your privacy and security requirements.\n\n## SAG vs. bLSAG Trade-offs\n\nThis library implements both a basic SAG-like ring signature and the bLSAG (Back's Linkable Spontaneous Anonymous Group) variant. They offer different properties with corresponding performance characteristics:\n\n**Functionality:**\n\n- **SAG (e.g., `sign`, `verify`, `sign_binary`, `verify_binary`):**\n  - Provides **Anonymity**: Hides which ring member produced the signature. The verifier only knows the signature came from _someone_ in the specified ring.\n  - Provides **Unlinkability**: Signatures produced by the same signer (for different messages or using different rings) cannot be cryptographically linked back to that signer or to each other.\n- **bLSAG (e.g., `sign_blsag_binary`, `verify_blsag_binary`):**\n  - Provides **Anonymity**: Same as SAG.\n  - Provides **Linkability**: Introduces a **Key Image** (`I`) which is unique and deterministic for each private key (`I = sk * H_p(PK)`). If the same private key is used to create multiple bLSAG signatures (even with different rings or messages), they will all produce the _same_ key image. This allows detection of multiple signatures from the same (anonymous) source, useful for preventing double-voting or double-spending in anonymous contexts. Signatures from _different_ private keys will produce _different_ key images.\n\n**Signature Size:**\n\n- **SAG Signature (`c0`, `s`):** Contains `n + 1` scalars (where `n` is the ring size).\n  - Binary Size: `32 * (n + 1)` bytes.\n- **bLSAG Signature (`c0`, `s`) + Key Image (`I`):** Contains `n + 1` scalars _plus_ one key image (a curve point).\n  - Binary Size: `[32 * (n + 1)]` bytes (signature) + `33` bytes (compressed key image) = `32n + 65` bytes.\n- **Comparison:** bLSAG signatures require transmitting the additional key image alongside the `c0` and `s` values, making them slightly larger (a constant overhead of 33 bytes compared to SAG when using compressed points).\n\n**Performance (Signing \u0026 Verification Speed):**\n\nThe computational cost is dominated by elliptic curve scalar multiplications and hashing operations.\n\n- **Elliptic Curve Operations:**\n  - **SAG:** Roughly `2n` point multiplications per sign/verify operation in the main loop (`s*G + c*P`).\n  - **bLSAG:** Roughly `4n` point multiplications per sign/verify operation in the main loop (`s*G + c*P` and `s*Hp(P) + c*I`). It also includes the key image calculation (`sk * Hp(PK)`) during signing and a key image validity check (subgroup check via `is_torsion_free`) during verification.\n- **Hashing:** -**SAG:** Uses one type of hash function (`hash_to_scalar`) involving the message, ring keys (hex strings in current implementation), and one point. This hash is computed `n` times per operation.\n  - **bLSAG:** Requires an additional `hash_to_point` operation (hashing a public key to a point) for each ring member (`n` times per operation). It uses a different challenge hash function (`hash_for_blsag_challenge`) involving the message and two points, also computed `n` times per operation.\n- **Comparison:** bLSAG signing and verification involve approximately twice the number of core point multiplications and additional hashing steps (`hash_to_point`). Therefore, bLSAG operations are expected to be noticeably **slower** than their SAG counterparts. We will provide detailed benchmarks to quantify this difference.\n\n**Summary Table:**\n\n| Feature         | SAG                  | bLSAG                         | Trade-off Summary                     |\n| :-------------- | :------------------- | :---------------------------- | :------------------------------------ |\n| **Linkability** | No (Unlinkable)      | Yes (Via Key Image)           | bLSAG adds same-signer detection.     |\n| **Size**        | `32(n+1)` bytes      | `32n + 65` bytes              | bLSAG is slightly larger (+33 bytes). |\n| **Speed**       | Faster (`~2n` mults) | Slower (`~4n` mults + extras) | bLSAG is computationally heavier.     |\n\n**When to Choose:**\n\n- Choose **SAG** if simple anonymity and unlinkability are sufficient, and maximum performance or minimum signature size are priorities.\n- Choose **bLSAG** if you **need** the ability to detect if the same anonymous signer has signed multiple times (e.g., voting, unique claims), and can accept the slightly larger signature size and increased computation time.\n\n## Installation\n\nAdd this crate to your `Cargo.toml` dependencies:\n\n```toml\n[dependencies]\nnostringer = \"0.1.0\" # Replace with the latest version from crates.io\n```\n\n_(Note: You might need other crates like `hex` or `rand` in your own project depending on how you handle keys and messages.)_\n\n## Usage\n\n```rust\nuse nostringer::{sign, verify, SignatureVariant, generate_keypair_hex, Error};\n\nfn main() -\u003e Result\u003c(), Error\u003e {\n    // 1. Setup: Generate keys for the ring members\n    // Keys can be x-only, compressed, or uncompressed hex strings\n    let keypair1 = generate_keypair_hex(\"xonly\");\n    let keypair2 = generate_keypair_hex(\"compressed\");\n    let keypair3 = generate_keypair_hex(\"xonly\");\n\n    let ring_pubkeys_hex: Vec\u003cString\u003e = vec![\n        keypair1.public_key_hex.clone(),\n        keypair2.public_key_hex.clone(), // Signer's key must be included\n        keypair3.public_key_hex.clone(),\n    ];\n\n    // 2. Define the message to be signed (as bytes)\n    let message = b\"This is a secret message to the group.\";\n\n    // 3. Signer (keypair2) signs the message using their private key\n    println!(\"Signing message...\");\n    \n    // Use the top-level API with compact signature format\n    // Choose the signature variant: SignatureVariant::Sag (unlinkable) or SignatureVariant::Blsag (linkable)\n    let signature = sign(\n        message,\n        \u0026keypair2.private_key_hex,        // Signer's private key hex\n        \u0026ring_pubkeys_hex,                // The full ring of public keys\n        SignatureVariant::Sag             // Use SAG variant (unlinkable)\n    )?;\n\n    println!(\"Generated Compact Signature: {}\", signature);\n    // Output is a compact \"ringA...\" format string\n\n    // 4. Verification: Anyone can verify the signature against the ring and message\n    println!(\"\\nVerifying signature...\");\n    let is_valid = verify(\n        \u0026signature,\n        message,\n        \u0026ring_pubkeys_hex, // Must use the exact same ring (order matters for hashing)\n    )?;\n\n    println!(\"Signature valid: {}\", is_valid);\n    assert!(is_valid);\n\n    // 5. Tamper test: Verification should fail if the message changes\n    println!(\"\\nVerifying with tampered message...\");\n    let tampered_message = b\"This is a different message.\";\n    let is_tampered_valid = verify(\n        \u0026signature,\n        tampered_message,\n        \u0026ring_pubkeys_hex,\n    )?;\n    println!(\"Tampered signature valid: {}\", is_tampered_valid);\n    assert!(!is_tampered_valid);\n\n    Ok(())\n}\n```\n\n### Using Different Signature Variants\n\nThe library provides two signature variants that you can select using the `SignatureVariant` enum:\n\n```rust\nuse nostringer::{sign, verify, SignatureVariant, generate_keypair_hex, Error};\n\nfn main() -\u003e Result\u003c(), Error\u003e {\n    // Setup: Generate keys for the ring\n    let keypair1 = generate_keypair_hex(\"xonly\");\n    let keypair2 = generate_keypair_hex(\"xonly\");\n    let ring = vec![keypair1.public_key_hex.clone(), keypair2.public_key_hex.clone()];\n    let message = b\"This is a message for the ring.\";\n    \n    // SAG variant (unlinkable - default)\n    // No way to tell if two signatures came from the same signer\n    let sag_signature = sign(\n        message,\n        \u0026keypair1.private_key_hex,\n        \u0026ring,\n        SignatureVariant::Sag // Use the SAG variant\n    )?;\n    \n    // BLSAG variant (linkable)\n    // Same key produces the same key image, allowing detection of repeat signers\n    let blsag_signature = sign(\n        message,\n        \u0026keypair1.private_key_hex,\n        \u0026ring,\n        SignatureVariant::Blsag // Use the BLSAG variant\n    )?;\n    \n    // Verify both types of signatures using the same verify function\n    // The signature format automatically determines which verification algorithm to use\n    assert!(verify(\u0026sag_signature, message, \u0026ring)?);\n    assert!(verify(\u0026blsag_signature, message, \u0026ring)?);\n    \n    Ok(())\n}\n```\n\n### Low-Level Binary API\n\nFor applications requiring maximum performance, we also provide lower-level binary APIs that work directly with the native types, avoiding hex conversion overhead:\n\n```rust\nuse nostringer::{sag, blsag, types::Error};\nuse k256::{Scalar, ProjectivePoint};\n\nfn main() -\u003e Result\u003c(), Error\u003e {\n    // Assuming you have raw binary keys available:\n    // (You'd normally get these from elsewhere in your app)\n    let private_key = /* Scalar value */;\n    let ring_pubkeys = /* Vec\u003cProjectivePoint\u003e */;\n    let message = b\"This is a secret message to the group.\";\n\n    // Sign using binary SAG API (more efficient)\n    let binary_signature = sag::sign_binary(message, \u0026private_key, \u0026ring_pubkeys, rand::rngs::OsRng)?;\n\n    // Verify using binary SAG API (more efficient)\n    let is_valid = sag::verify_binary(\u0026binary_signature, message, \u0026ring_pubkeys)?;\n    println!(\"Signature valid: {}\", is_valid);\n\n    Ok(())\n}\n```\n\n### WebAssembly Usage\n\nNostringer can be compiled to WebAssembly, allowing you to use it directly in web browsers and other WASM environments:\n\n```javascript\n// Import the WASM module\nimport init, {\n  wasm_generate_keypair,\n  wasm_sign,\n  wasm_verify,\n  wasm_sign_blsag,\n  wasm_verify_blsag,\n  wasm_key_images_match,\n} from \"./nostringer.js\";\n\n// Initialize the WASM module\nasync function main() {\n  await init();\n\n  // Generate keypairs for the ring\n  const keypair1 = wasm_generate_keypair(\"xonly\");\n  const keypair2 = wasm_generate_keypair(\"xonly\");\n  const keypair3 = wasm_generate_keypair(\"xonly\");\n\n  const ringPubkeys = [\n    keypair1.public_key_hex(),\n    keypair2.public_key_hex(),\n    keypair3.public_key_hex(),\n  ];\n\n  // Sign a message with one of the keys\n  const message = new TextEncoder().encode(\n    \"This is a secret message to the group.\",\n  );\n  const signature = wasm_sign(message, keypair2.private_key_hex(), ringPubkeys);\n\n  // Verify the signature\n  const isValid = wasm_verify(signature, message, ringPubkeys);\n  console.log(\"Signature valid:\", isValid);\n}\n\nmain();\n```\n\n#### Building for WASM\n\nTo compile Nostringer for WebAssembly:\n\n```bash\n# Install wasm-pack if you don't have it\ncargo install wasm-pack\n\n# Build the WASM module\nwasm-pack build --target web --features wasm\n\n# For bundlers like webpack\nwasm-pack build --target bundler --features wasm\n\n# For Node.js\nwasm-pack build --target nodejs --features wasm\n```\n\nSee the [WebAssembly example](https://github.com/AbdelStark/nostringer-rs/tree/main/crates/nostringer/examples/web/basic_wasm) for a complete demonstration of using Nostringer in a web browser.\n\n## Examples\n\nThe repository includes several examples that demonstrate different aspects of the library:\n\n1. **Basic Signing** (`examples/basic_signing.rs`): Demonstrates the core signing and verification functionality.\n\n   ```bash\n   cargo run --example basic_signing\n   ```\n\n2. **Key Formats** (`examples/key_formats.rs`): Shows how to work with different key formats (x-only, compressed, uncompressed) and create larger rings.\n\n   ```bash\n   cargo run --example key_formats\n   ```\n\n3. **BLSAG Linkability** (`examples/blsag_linkability.rs`): Demonstrates the linkable BLSAG variant and how to detect when the same key is used for multiple signatures.\n\n   ```bash\n   cargo run --example blsag_linkability\n   ```\n\n4. **Error Handling** (`examples/error_handling.rs`): Demonstrates proper error handling for common error scenarios.\n\n   ```bash\n   cargo run --example error_handling\n   ```\n\n5. **WebAssembly** (`examples/web/basic_wasm`): A web-based example showing how to use the library in a browser via WebAssembly.\n\n   ```bash\n   # Build the WASM module\n   wasm-pack build crates/nostringer --target web --out-dir examples/web/basic_wasm/pkg --features wasm\n\n   # Serve the example (using Python's built-in server)\n   cd crates/nostringer/examples/web/basic_wasm\n   python -m http.server\n   ```\n\nThese examples provide practical demonstrations of how to use the library in real-world scenarios and handle various edge cases.\n\n## Benchmarks\n\nThe library includes comprehensive benchmarks using the Criterion framework for different ring sizes and operations. You can run these benchmarks yourself with:\n\n```bash\ncargo bench\n```\n\nFor detailed information on running and interpreting benchmarks, see [BENCHMARKS.md](BENCHMARKS.md).\n\nThe repository also includes a GitHub Actions workflow that automatically runs benchmarks on each push and pull request, with the HTML report available as an artifact in the workflow run.\n\n### Performance Results\n\nBelow is a summary of the benchmark results, showing median execution times for each operation with different ring sizes:\n\n| Operation       | Ring Size   | Execution Time |\n| --------------- | ----------- | -------------- |\n| **Sign**        | 2 members   | 204.75 µs      |\n| **Sign**        | 10 members  | 897.76 µs      |\n| **Sign**        | 100 members | 13.31 ms       |\n| **Verify**      | 2 members   | 166.83 µs      |\n| **Verify**      | 10 members  | 847.23 µs      |\n| **Verify**      | 100 members | 12.71 ms       |\n| **Sign+Verify** | 2 members   | 370.41 µs      |\n| **Sign+Verify** | 10 members  | 1.76 ms        |\n| **Sign+Verify** | 100 members | 25.02 ms       |\n\nBenchmarking Environment:\n\n- **Model:** MacBook Pro (Identifier: `MacBookPro18,2`)\n- **CPU:** Apple M1 Max\n- **Cores:** 10\n- **RAM:** 64 GB\n- **Architecture:** `arm64`\n- **Operating System:** macOS 14.7 (Build `23H124`)\n\n## API Reference\n\nCheck the [Rust API Docs](https://docs.rs/nostringer/latest/nostringer/) for detailed API reference and usage examples.\n\n## Signature Size\n\nThe size of the generated ring signature depends directly on the number of members (`n`) in the ring. It consists of:\n\n- One initial challenge (`c0`) scalar (32 bytes binary / 64 hex chars).\n- `n` response scalars (`s` array) (each 32 bytes binary / 64 hex chars).\n\nThe total **binary size** follows the formula:\n`Size (bytes) = 32 * (n + 1)`\n\nThis means the signature size grows **linearly** with the ring size. A larger ring provides more anonymity but results in a larger signature.\n\n## Security Considerations\n\n- **Anonymity Set**: The level of anonymity depends on the size (`n`) and plausibility of the chosen ring members. Ensure the ring contains keys that could _realistically_ be the signer in the given context.\n- **No Trusted Setup**: This scheme does not require any trusted setup procedure.\n- **Unlinkability vs. Linkability**:\n  - **SAG**: The default SAG implementation provides complete unlinkability. Signatures produced by the same signer for different messages (using the same or different rings) are cryptographically unlinkable.\n  - **BLSAG**: The BLSAG variant intentionally provides linkability through key images. These key images allow detecting when the same key signed multiple messages, while still preserving anonymity (not revealing which specific ring member is the signer).\n- **Implementation Security**: This library relies on the correctness of the underlying `k256` crate. While `k256` is well-regarded, this specific ring signature implementation has **not** been independently audited.\n\n## Disclaimer\n\n\u003e **This code is highly experimental.**\n\u003e The original author is not a cryptographer, and this Rust port, while aiming for compatibility and correctness using standard libraries, **has not been audited or formally verified.**\n\u003e Use for educational exploration **at your own risk.** Production usage is **strongly discouraged** until thorough security reviews and testing are performed by qualified individuals.\n\n## License\n\nThis project is licensed under the [MIT License](LICENSE).\n\n## References\n\n- [Linkable Spontaneous Anonymous Group Signature for Ad Hoc Groups](https://eprint.iacr.org/2004/027.pdf) - (Joseph Liu et al., 2004) – basis of LSAG.\n- [Beritani, ring-signatures JS library](https://github.com/beritani/ring-signatures) – Ed25519 ring signature implementation (SAG, bLSAG, MLSAG, CLSAG)​.\n- [Blockstream Elements rust-secp256k1-zkp library](https://github.com/BlockstreamResearch/rust-secp256k1-zkp) – Whitelist Ring Signature in libsecp256k1-zkp (C code exposed via Rust)​.\n- [Zero to Monero 2.0 – Chapter 3, ring signature algorithms](https://www.getmonero.org/library/Zero-to-Monero-2-0-0.pdf).\n- [Cronokirby Blog – On Monero's Ring Signatures](https://cronokirby.com/posts/2022/03/on-moneros-ring-signatures), explains Schnorr ring signatures in detail​.\n\n---\n\nBuilt with love by [AbdelStark](https://github.com/AbdelStark) 🧡\n\nFeel free to follow me on Nostr if you'd like, using my public key:\n\n```text\nnpub1hr6v96g0phtxwys4x0tm3khawuuykz6s28uzwtj5j0zc7lunu99snw2e29\n```\n\nOr just **scan this QR code** to find me:\n\n![Nostr Public Key QR Code](https://hackmd.io/_uploads/SkAvwlYYC.png)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabdelstark%2Fnostringer-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fabdelstark%2Fnostringer-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabdelstark%2Fnostringer-rs/lists"}