{"id":50311749,"url":"https://github.com/midnight-wonderer/xoodyak-rb","last_synced_at":"2026-05-28T21:30:48.480Z","repository":{"id":359473392,"uuid":"1245860597","full_name":"midnight-wonderer/xoodyak-rb","owner":"midnight-wonderer","description":"A blazing fast Ruby wrapper for the Xoodyak cryptographic scheme. Supports hashing and AEAD encryption.","archived":false,"fork":false,"pushed_at":"2026-05-22T04:46:30.000Z","size":40,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-22T11:43:40.700Z","etag":null,"topics":["aead","cipher","cryptography","digest","gem","hash","lightweight-cryptography","ruby","sponge-construction","xoodyak"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/midnight-wonderer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","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,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-21T16:19:51.000Z","updated_at":"2026-05-22T04:46:33.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/midnight-wonderer/xoodyak-rb","commit_stats":null,"previous_names":["midnight-wonderer/xoodyak-rb"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/midnight-wonderer/xoodyak-rb","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/midnight-wonderer%2Fxoodyak-rb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/midnight-wonderer%2Fxoodyak-rb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/midnight-wonderer%2Fxoodyak-rb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/midnight-wonderer%2Fxoodyak-rb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/midnight-wonderer","download_url":"https://codeload.github.com/midnight-wonderer/xoodyak-rb/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/midnight-wonderer%2Fxoodyak-rb/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33627934,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-28T02:00:06.440Z","response_time":99,"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":["aead","cipher","cryptography","digest","gem","hash","lightweight-cryptography","ruby","sponge-construction","xoodyak"],"created_at":"2026-05-28T21:30:47.629Z","updated_at":"2026-05-28T21:30:48.465Z","avatar_url":"https://github.com/midnight-wonderer.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Xoodyak Ruby Gem\n\nA blazing fast, secure, and modern Rust-backed Ruby implementation of the Xoodyak cryptographic scheme.\n\n[![Gem Version](https://img.shields.io/gem/v/xoodyak.svg?style=flat-square)](https://badge.fury.io/rb/xoodyak)\n[![License](https://img.shields.io/badge/license-BSD--2--Clause-blue.svg?style=flat-square)](https://github.com/midnight-wonderer/xoodyak-rb/blob/main/LICENSE.md)\n[![RBS Types](https://img.shields.io/badge/types-RBS-informational.svg?style=flat-square)](https://github.com/midnight-wonderer/xoodyak-rb/blob/main/sig/xoodyak.rbs)\n\n---\n\n## 📖 Table of Contents\n\n- [Introduction](#-introduction)\n- [Features](#-features)\n- [Installation](#-installation)\n- [Usage Guide](#-usage-guide)\n  - [1. Hashing (Unkeyed Mode)](#1-hashing-unkeyed-mode)\n  - [2. Ruby Digest API Integration](#2-ruby-digest-api-integration)\n  - [3. Symmetric Encryption (Keyed Mode)](#3-symmetric-encryption-keyed-mode)\n  - [4. Authenticated Encryption (AEAD)](#4-authenticated-encryption-aead)\n    - [Combined Ciphertext \u0026 Tag](#combined-ciphertext--tag)\n    - [Detached Ciphertext \u0026 Tag](#detached-ciphertext--tag)\n  - [5. Advanced Keyed Customization (Nonces, Key IDs, Counters)](#5-advanced-keyed-customization-nonces-key-ids-counters)\n  - [6. Forward Secrecy (State Ratcheting)](#6-forward-secrecy-state-ratcheting)\n  - [7. Stateful Session-based Encrypt/Decrypt](#7-stateful-session-based-encryptdecrypt)\n  - [8. State Cloning \u0026 Checkpointing](#8-state-cloning--checkpointing)\n- [API Reference](#-api-reference)\n- [Type Safety with RBS](#-type-safety-with-rbs)\n- [Development \u0026 Testing](#-development--testing)\n- [License](#-license)\n\n---\n\n## 🌟 Introduction\n\n**Xoodyak** is a lightweight cryptographic scheme designed by the Keccak team (creators of SHA-3). It is part of the Keccak family and is optimized for low-resource environments. Xoodyak operates as a stateful \"sponge\" construction, making it extremely versatile. A single instance can perform:\n- **Hashing** (unkeyed mode)\n- **Symmetric Encryption** (keyed mode)\n- **Message Authentication Codes (MAC)** (keyed mode)\n- **Authenticated Encryption with Associated Data (AEAD)** (keyed mode)\n- **Key Derivation \u0026 Ratcheting**\n\nThis gem provides a production-ready Ruby interface to Xoodyak, wrapping a highly optimized Rust implementation.\n\n---\n\n## ⚡ Features\n\n- 🏎️ **Blazing Fast**: Native Rust extension using `magnus` and `rb-sys` outpaces pure Ruby cryptography.\n- 🔒 **Sponge-based Design**: Supports stateful session-based protocols.\n- 🛠️ **Seamless Digest Integration**: Inherits from Ruby's standard `Digest::Base` for drop-in compatibility.\n- 📦 **Zero-Configuration AEAD**: Simple combined and detached AEAD interfaces.\n- 🧩 **RBS Typed**: Complete type definitions shipped out of the box.\n- 🛡️ **Memory Safe**: Built-in Rust safety guarantees prevent common memory leaks and buffer overflows.\n\n---\n\n## 📥 Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'xoodyak'\n```\n\nAnd then execute:\n\n```bash\n$ bundle install\n```\n\nOr install it directly via:\n\n```bash\n$ gem install xoodyak\n```\n\n\u003e [!NOTE]\n\u003e Since this gem includes a Rust extension, you must have the **Rust toolchain** (cargo/rustc) installed on your system to compile it.\n\n---\n\n## 🚀 Usage Guide\n\n### 1. Hashing (Unkeyed Mode)\n\nIn unkeyed mode, Xoodyak acts as a standard cryptographic hash function. You can feed data incrementally using `absorb` and extract the hash using `squeeze`.\n\n```ruby\nrequire 'xoodyak'\n\n# Initialize in unkeyed (hashing) mode\nhash_sponge = Xoodyak.new\n\n# Absorb data\nhash_sponge.absorb(\"Hello, world!\")\n\n# Squeeze out the digest (you can request any length!)\ndigest = hash_sponge.squeeze(32)\n# =\u003e returns a 32-byte binary string\n```\n\n### 2. Ruby Digest API Integration\n\nFor standard hashing tasks, this gem integrates directly with Ruby's `Digest` framework.\n\n```ruby\nrequire 'xoodyak'\n\n# 1. Instantiate the Digest class\ndigest = Xoodyak::Digest.new\ndigest.update(\"Hello, \")\ndigest.update(\"world!\")\nputs digest.hexdigest\n# =\u003e \"c1ae6b98...\"\n\n# 2. Or use the shortcut methods\nhex_hash = Digest::Xoodyak.hexdigest(\"Hello, world!\")\nbinary_hash = Digest::Xoodyak.digest(\"Hello, world!\")\n\n# 3. Dynamic loading also works\nalgo = Digest(\"Xoodyak\").new\n```\n\n### 3. Symmetric Encryption (Keyed Mode)\n\nBy passing a key during initialization, Xoodyak enters **keyed mode**. This allows standard symmetric encryption and decryption.\n\n```ruby\nrequire 'xoodyak'\n\nkey = \"my-secure-key-16\" # Can be any length (Xoodyak handles varying key lengths)\n\n# Encrypting\nencryptor = Xoodyak.new(key)\nciphertext = encryptor.encrypt(\"super secret message\")\n\n# Decrypting (initialize a new state with the same key)\ndecryptor = Xoodyak.new(key)\nplaintext = decryptor.decrypt(ciphertext)\nputs plaintext # =\u003e \"super secret message\"\n```\n\n### 4. Authenticated Encryption (AEAD)\n\nStandard encryption protects confidentiality but not integrity. **AEAD** (Authenticated Encryption with Associated Data) is highly recommended because it also authenticates the message and optional \"Associated Data\" (like unencrypted routing headers).\n\n#### Combined Ciphertext \u0026 Tag\n\n`aead_encrypt` appends a 16-byte authentication tag directly to the ciphertext. `aead_decrypt` verifies the tag and returns the decrypted text, raising an error if the tag is invalid.\n\n```ruby\nrequire 'xoodyak'\nrequire 'securerandom'\n\nkey = \"my-secure-key-16\"\nnonce = SecureRandom.bytes(16) # Nonces must be unique for each encryption!\n\n# Encrypt with Associated Data\nalice = Xoodyak.new(key, nonce: nonce)\nalice.absorb(\"Associated Data (unencrypted header)\")\nciphertext_with_tag = alice.aead_encrypt(\"confidential message\")\n\n# Decrypt and Verify\nbob = Xoodyak.new(key, nonce: nonce)\nbob.absorb(\"Associated Data (unencrypted header)\") # Must match Alice's AD\n\nbegin\n  decrypted = bob.aead_decrypt(ciphertext_with_tag)\n  puts decrypted # =\u003e \"confidential message\"\nrescue Xoodyak::Error =\u003e e\n  # Raised if ciphertext or associated data was altered\n  puts \"Integrity check failed: #{e.message}\"\nend\n```\n\n#### Detached Ciphertext \u0026 Tag\n\nIf your protocol stores or transmits the ciphertext and tag separately, you can use the detached API:\n\n```ruby\nrequire 'xoodyak'\nrequire 'securerandom'\n\nkey = \"my-secure-key-16\"\nnonce = SecureRandom.bytes(16)\n\n# Encrypt\nalice = Xoodyak.new(key, nonce: nonce)\nalice.absorb(\"metadata\")\nciphertext, tag = alice.aead_encrypt_detached(\"confidential message\")\n\n# Decrypt and Verify\nbob = Xoodyak.new(key, nonce: nonce)\nbob.absorb(\"metadata\")\n\nbegin\n  decrypted = bob.aead_decrypt_detached(ciphertext, tag)\n  puts decrypted # =\u003e \"confidential message\"\nrescue Xoodyak::Error =\u003e e\n  puts \"Integrity check failed: #{e.message}\"\nend\n```\n\n### 5. Advanced Keyed Customization (Nonces, Key IDs, Counters)\n\nXoodyak supports initializing the keyed state with a variety of optional parameters:\n- `key` (required for keyed mode)\n- `nonce` (optional binary string)\n- `key_id` (optional binary string)\n- `counter` (optional binary string)\n\n```ruby\n# Initialize with key, nonce, key_id, and counter\nxoodyak = Xoodyak.new(key, nonce: nonce, key_id: key_id, counter: counter)\n```\n\n\u003e [!WARNING]\n\u003e Passing `nonce`, `key_id`, or `counter` without a `key` will raise an `ArgumentError`.\n\n### 6. Forward Secrecy (State Ratcheting)\n\nState ratcheting advances the keyed state in a non-reversible way. Even if an attacker gains access to the current state, they cannot reconstruct past states, providing forward secrecy.\n\n```ruby\nrequire 'xoodyak'\n\nxoodyak = Xoodyak.new(\"my-secret-key\")\n\n# Perform operations...\nxoodyak.absorb(\"some context\")\n\n# Ratchet the state\nxoodyak.ratchet\n\n# Squeeze out session keys or continue encrypting\nsession_key = xoodyak.squeeze(32)\n```\n\n### 7. Stateful Session-based Encrypt/Decrypt\n\nXoodyak is stateful: every operation transitions the internal sponge state. This allows Bob and Alice to have a stateful session where they encrypt and decrypt a stream of messages in order.\n\n```ruby\nrequire 'xoodyak'\nrequire 'securerandom'\n\nkey = \"session-key-1234\"\nnonce = SecureRandom.bytes(16)\n\nalice = Xoodyak.new(key, nonce: nonce)\nbob = Xoodyak.new(key, nonce: nonce)\n\n# Alice sends first message\nct1 = alice.encrypt(\"Message 1\")\n# Bob receives and decrypts\nputs bob.decrypt(ct1) # =\u003e \"Message 1\"\n\n# Alice sends second message (depends on state mutated by msg1!)\nct2 = alice.encrypt(\"Message 2\")\n# Bob decrypts\nputs bob.decrypt(ct2) # =\u003e \"Message 2\"\n```\n\n\u003e [!IMPORTANT]\n\u003e Because the state mutates with each operation, Alice and Bob must remain in perfect sync. If any message is lost, reordered, or duplicated, decryption will fail. This provides built-in replay and out-of-order protection.\n\n### 8. State Cloning \u0026 Checkpointing\n\nYou can duplicate or clone the state of a Xoodyak instance. This is useful for saving checkpoints or branching a cryptographic session.\n\n```ruby\nrequire 'xoodyak'\n\nxoodyak = Xoodyak.new\nxoodyak.absorb(\"initial setup data\")\n\n# Duplicate the state\ncheckpoint = xoodyak.dup\n\n# Both instances can now diverge independently\nxoodyak.absorb(\"branch A\")\ncheckpoint.absorb(\"branch B\")\n\nputs xoodyak.squeeze(16).unpack1(\"H*\")      # Squeezes based on \"initial setup data\" + \"branch A\"\nputs checkpoint.squeeze(16).unpack1(\"H*\")   # Squeezes based on \"initial setup data\" + \"branch B\"\n```\n\n---\n\n## 🛠️ API Reference\n\n### `Xoodyak` Class\n\n| Method | Signature | Mode | Description |\n| :--- | :--- | :--- | :--- |\n| `initialize` | `(key=nil, nonce: nil, key_id: nil, counter: nil)` | Any | Creates a Xoodyak instance. Enters keyed mode if a key is provided. |\n| `absorb` | `(bin: String) -\u003e void` | Any | Absorbs binary data into the state. |\n| `squeeze` | `(len: Integer) -\u003e String` | Any | Squeezes `len` bytes from the state. |\n| `squeeze_key` | `(len: Integer) -\u003e String` | Any | Squeezes `len` key bytes from the state. |\n| `encrypt` | `(bin: String) -\u003e String` | Keyed | Encrypts a message. |\n| `decrypt` | `(bin: String) -\u003e String` | Keyed | Decrypts a message. |\n| `aead_encrypt` | `(bin: String) -\u003e String` | Keyed | Encrypts a message, appending a 16-byte authentication tag. |\n| `aead_decrypt` | `(bin: String) -\u003e String` | Keyed | Verifies the tag and decrypts a combined AEAD message. |\n| `aead_encrypt_detached` | `(bin: String) -\u003e [String, String]` | Keyed | Encrypts a message, returning `[ciphertext, tag]`. |\n| `aead_decrypt_detached` | `(bin: String, tag: String) -\u003e String` | Keyed | Verifies the detached tag and decrypts the ciphertext. |\n| `ratchet` | `() -\u003e void` | Keyed | Ratchets the state to provide forward secrecy. |\n| `dup` / `clone` | `() -\u003e Xoodyak` | Any | Creates a deep copy of the Xoodyak instance state. |\n\n---\n\n## 🧩 Type Safety with RBS\n\nThis gem is packaged with complete RBS type definitions. You can typecheck your application using Steep or other Ruby signature verification tools.\n\nType signatures are defined in [sig/xoodyak.rbs](file:///storage/projects/xoodyak-rb/sig/xoodyak.rbs).\n\n---\n\n## 🔧 Development \u0026 Testing\n\nAfter checking out the repo, run `bin/setup` to install dependencies.\n\n### Compilation\n\nSince the core cryptographic operations are written in Rust, you must compile the C-extension locally:\n\n```bash\nbundle exec rake compile\n```\n\n### Running Tests\n\nRun the RSpec test suite:\n\n```bash\nbundle exec rake spec\n```\n\n### Linting\n\nCheck code formatting and style guidelines:\n\n```bash\nbundle exec rake rubocop\n```\n\n---\n\n## 📄 License\n\nThis gem is available as open source under the terms of the [BSD 2-Clause License](LICENSE.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmidnight-wonderer%2Fxoodyak-rb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmidnight-wonderer%2Fxoodyak-rb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmidnight-wonderer%2Fxoodyak-rb/lists"}