diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 91c5ee3..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,5 +0,0 @@ -## [Unreleased] - -## [0.1.0] - 2023-09-19 - -- Initial release diff --git a/README.md b/README.md index c63c37f..14178ee 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,97 @@ -# Actionhash +# ActionHash -TODO: Delete this and the text below, and describe your gem +## Overview -Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/actionhash`. To experiment with that code, run `bin/console` for an interactive prompt. +ActionHash is a Ruby gem designed to validate a series of actions while obfuscating the associated data. It's particularly useful in scenarios where you want to ensure the integrity of a sequence of actions or data points, such as in gaming, financial transactions, IoT devices, and more. + +> :warning: **Security Note**: While ActionHash does obfuscate data, it should not be considered a form of encryption. If someone gains access to the generated secret key, the hashes can be undone. Use this gem as a part of your security strategy, not as the sole mechanism for securing sensitive information. ## Installation -TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org. +```bash +gem install actionhash +``` -Install the gem and add to the application's Gemfile by executing: +Or add it to your Gemfile: - $ bundle add UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG +```ruby +gem 'actionhash' +``` -If bundler is not being used to manage dependencies, install the gem by executing: +Then run: - $ gem install UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG +```bash +bundle install +``` -## Usage +## Basic Usage -TODO: Write usage instructions here +Here's a simple example to get you started: -## Development +```ruby +require 'actionhash' -After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. +# Initialize +keys = [ActionHash.generate_new_key] +hashes = [] # Array to store finished hashes +action_count = 0 +prev_hash = '0' +data = "some_data" -To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org). +# Create a hash +new_hash = ActionHash.create(prev_hash, data, keys.last) +hashes << new_hash # Store the hash +``` -## Contributing +### Validating a Hash -Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/actionhash. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/actionhash/blob/master/CODE_OF_CONDUCT.md). +You can validate a hash using the `valid_hash?` method as follows: -## License +```ruby +# Validate the hash +is_valid = ActionHash.valid_hash?(hashes.last, keys.last, 20) -The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). +if is_valid + puts "Hash is valid." +else + puts "Hash is invalid." +end +``` -## Code of Conduct +The `valid_hash?` method works by recursively decrypting the hash and checking if it reaches a base hash of '0'. It takes the hash, the key, and an optional level parameter to limit the depth of validation. + +Here's the method definition for your reference: + +```ruby +def self.valid_hash?(hash, key, level = 20) + current_level = 0 + loop do + puts "Debug: Validating hash=#{hash} with key=#{key} at level=#{current_level}" + return false if current_level >= level + + decrypted_data = down_layer(hash, key) + return true if decrypted_data[:prev_hash] == '0' + return false if decrypted_data[:prev_hash].nil? || decrypted_data[:prev_hash].empty? + + hash = decrypted_data[:prev_hash] + current_level += 1 + end +end +``` + +## Development Builds + +For those interested in using development builds of ActionHash, you can install them directly from our Gem server hosted at PixelRidge Softworks. + +Here's how you can install a development build: + +```bash +gem install ActionHash --source "https://git.pixelridgesoftworks.com/api/packages/PixelRidge-Softworks/rubygems" +``` + +As an example, if you want to install a different gem from our Gem server, you can use the following command: + +```bash +gem install Miniparser --source "https://git.pixelridgesoftworks.com/api/packages/PixelRidge-Softworks/rubygems" +``` -Everyone interacting in the Actionhash project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/actionhash/blob/master/CODE_OF_CONDUCT.md). diff --git a/actionhash.gemspec b/actionhash.gemspec index 18ae3e2..25c531c 100644 --- a/actionhash.gemspec +++ b/actionhash.gemspec @@ -9,7 +9,8 @@ Gem::Specification.new do |spec| spec.email = ['ceo@pixelridgesoftworks.com'] spec.summary = 'A custom hash mechanism for action tracking and validation.' - spec.description = 'ActionHash provides a way to create and validate custom hashes for tracking user actions in a secure and efficient manner.' + spec.description = 'ActionHash provides a way to create and validate custom hashes for tracking user actions in a + secure and efficient manner.' spec.homepage = 'https://git.pixelridgesoftworks.com/PixelRidge-Softworks/ActionHash' spec.license = 'PixelRidge-BEGPULSE' spec.required_ruby_version = '>= 3.2.2' @@ -27,7 +28,4 @@ Gem::Specification.new do |spec| spec.bindir = 'exe' spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } spec.require_paths = ['lib'] - - # Uncomment to register a new dependency of your gem - spec.add_dependency "dotenv", "~> 2.7" end diff --git a/lib/actionhash.rb b/lib/actionhash.rb index e803e1c..d9b0c2a 100644 --- a/lib/actionhash.rb +++ b/lib/actionhash.rb @@ -1,28 +1,68 @@ # frozen_string_literal: true require_relative 'actionhash/version' +require 'securerandom' -# main module module ActionHash class Error < StandardError; end + @key_usage_count = {} + MAX_ACTIONS_PER_KEY = 10 + + # Generate a new key + def self.generate_new_key + SecureRandom.hex(8) + end + # Create a new Action Hash def self.create(prev_hash, input_data, key) + # Check if the key has reached its limit + @key_usage_count[key] ||= 0 + if @key_usage_count[key] >= MAX_ACTIONS_PER_KEY + raise Error, 'Key has reached its maximum usage. Please generate a new key.' + end + data = [prev_hash, input_data].join(',') + puts "Debug: Creating hash with prev_hash=#{prev_hash}, input_data=#{input_data}, key=#{key}" + puts "Concatenated data before encryption: #{data}" encrypted_data = xor_encrypt(data, key) - encrypted_data.unpack1('H*') # Convert to hex + encrypted_hex = encrypted_data.unpack1('H*') # Convert to hex + puts "Encrypted hex: #{encrypted_hex}" + + # Increment the usage count for the key + @key_usage_count[key] += 1 + + encrypted_hex end # Decrypt an Action Hash to its components def self.down_layer(hash, key) + puts "Debug: Decrypting hash=#{hash} with key=#{key}" hex_decoded = [hash].pack('H*') # Convert from hex decrypted_data = xor_encrypt(hex_decoded, key) + puts "Decrypted data: #{decrypted_data}" prev_hash, input_data = decrypted_data.split(',') - { prev_hash: prev_hash.to_i, input_data: input_data.to_i } + { prev_hash: prev_hash.to_s, input_data: input_data.to_s } end # XOR encrypt/decrypt (symmetric) def self.xor_encrypt(data, key) data.bytes.zip(key.bytes.cycle).map { |a, b| (a ^ b).chr }.join end + + # Validate if a hash can be traced back to '0' + def self.valid_hash?(hash, key, level = 20) + current_level = 0 + loop do + puts "Debug: Validating hash=#{hash} with key=#{key} at level=#{current_level}" + return false if current_level >= level + + decrypted_data = down_layer(hash, key) + return true if decrypted_data[:prev_hash] == '0' + return false if decrypted_data[:prev_hash].nil? || decrypted_data[:prev_hash].empty? + + hash = decrypted_data[:prev_hash] + current_level += 1 + end + end end diff --git a/tests/hash_generator.rb b/tests/hash_generator.rb new file mode 100644 index 0000000..0266a3a --- /dev/null +++ b/tests/hash_generator.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +require 'actionhash' # Update this path to where your ActionHash module is located + +class ActionHashTest + def initialize + @keys = [ActionHash.generate_new_key] # Initialize an array to store keys + puts "Generated key: #{@keys.last}" + @hashes = [] + @action_count = 0 + end + + def run_test(depth) + prev_hash = '0' + data = 30 + + depth.times do |i| + puts "Layer #{i + 1}:" + + # Check if the key has reached its limit and generate a new one if necessary + if @action_count >= ActionHash::MAX_ACTIONS_PER_KEY + @keys << ActionHash.generate_new_key # Generate and store a new key + puts "Generated new key: #{@keys.last}" + @action_count = 0 + prev_hash = '0' # Reset prev_hash for the new key + end + + current_key = @keys.last # Use the last key in the array + new_hash = ActionHash.create(prev_hash, data.to_s, current_key) + puts "Generated hash: #{new_hash}" + @hashes << { hash: new_hash, key: current_key } + prev_hash = new_hash + data *= 2 + + @action_count += 1 + end + + validate_hashes + end + + def validate_hashes + puts "\nValidating hashes..." + all_valid = true + prev_hash = '0' # Initialize prev_hash to '0' as in the run_test method + current_key_index = 0 # Initialize index to keep track of the current key + + @hashes.each_with_index do |hash_data, index| + hash = hash_data[:hash] + key = hash_data[:key] + + # Check if we need to switch to the next key + if current_key_index < @keys.length - 1 && key != @keys[current_key_index] + current_key_index += 1 + prev_hash = '0' # Reset prev_hash for the new key + end + + decrypted_data = ActionHash.down_layer(hash, key) + puts "Decrypted data: #{decrypted_data[:prev_hash]},#{decrypted_data[:input_data]}" + puts "Current level: #{index + 1}" + puts "Current hash: #{hash}" + + if decrypted_data[:prev_hash] != prev_hash + puts "Hash at level #{index + 1} is invalid." + all_valid = false + break + end + + prev_hash = hash # Update prev_hash for the next iteration + end + + puts all_valid ? 'All hashes are valid.' : 'Hash validation failed.' + end +end + +if ARGV.length != 1 || !ARGV[0].match?(/^\d+$/) + puts "Usage: ruby #{__FILE__} [depth]" + exit 1 +end + +depth = ARGV[0].to_i +test = ActionHashTest.new +test.run_test(depth)