obfuskey 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9bc5f45d8820d179791885eba40fc30d7cf1145e1f62194f3354fc9bac512f90
4
+ data.tar.gz: ea7b2365b2c97baec0f8368a087ea461bfd47dd0e6250d94e1278c70a93b7903
5
+ SHA512:
6
+ metadata.gz: 174c3b7d704769bea2c6d10ee3f25c557c395932bcc3d66f5d6a0a3c0b139b429c81856810d11ce29aa17be6f3f361b6a2258fec0394b9123e90a0796f813bb4
7
+ data.tar.gz: 221acb4d5cf61847afb3874ab4dd81a743c08f4e133fa48b6264d4bcbdca5bbd08d64159fa7f1f8e05739c3934f809b32219630c91632a076cdb8d194592e517
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Nathan Lucas
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # obfuskey-rb
2
+
3
+ A Ruby port of [Obfuskey](https://github.com/bnlucas/obfuskey). Generates deterministic, reversible, fixed-length keys from integer values using a custom alphabet. Cross-compatible with the Python, JavaScript, and Rust implementations — the same `(alphabet, key_length, value)` triple produces the same key in every language.
4
+
5
+ ## Install
6
+
7
+ ```ruby
8
+ # Gemfile
9
+ gem "obfuskey"
10
+ ```
11
+
12
+ ## Usage
13
+
14
+ ```ruby
15
+ require "obfuskey"
16
+
17
+ obf = Obfuskey.new(Obfuskey::Alphabets::BASE62)
18
+ key = obf.get_key(12345) # => "d2Aasl"
19
+ obf.get_value(key) # => 12345
20
+ ```
21
+
22
+ ### Custom key length or multiplier
23
+
24
+ ```ruby
25
+ Obfuskey.new(Obfuskey::Alphabets::BASE62, key_length: 8)
26
+ Obfuskey.new(Obfuskey::Alphabets::BASE62, multiplier: 123) # odd integer
27
+ ```
28
+
29
+ ### Obfusbit (bit-packed structured keys)
30
+
31
+ ```ruby
32
+ schema = [
33
+ { name: "id", bits: 10 },
34
+ { name: "type", bits: 2 },
35
+ { name: "flag", bits: 1 }
36
+ ]
37
+
38
+ key_maker = Obfuskey.new(Obfuskey::Alphabets::BASE62, key_length: 3)
39
+ ob = Obfusbit.new(schema, obfuskey: key_maker)
40
+
41
+ encoded = ob.pack({ "id" => 100, "type" => 2, "flag" => 1 }, obfuscate: true)
42
+ ob.unpack(encoded, obfuscated: true)
43
+ # => { "id" => 100, "type" => 2, "flag" => 1 }
44
+ ```
45
+
46
+ ## Alphabets
47
+
48
+ `Obfuskey::Alphabets` provides `BASE16`, `BASE32`, `BASE36`, `BASE52`, `BASE56`, `BASE58`, `BASE62`, `BASE64`, `BASE94`, `CROCKFORD_BASE32`, `ZBASE32`, `BASE64_URL_SAFE`.
49
+
50
+ ## Tests
51
+
52
+ ```
53
+ bundle install
54
+ bundle exec rspec
55
+ ```
56
+
57
+ ## License
58
+
59
+ MIT
@@ -0,0 +1,25 @@
1
+ class Obfuskey
2
+ module Alphabets
3
+ BASE16 = "0123456789ABCDEF".freeze
4
+ BASE32 = "234567ABCDEFGHIJKLMNOPQRSTUVWXYZ".freeze
5
+ BASE36 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".freeze
6
+ BASE52 = "0123456789BCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz".freeze
7
+ BASE56 = "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz".freeze
8
+ BASE58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".freeze
9
+ BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".freeze
10
+ BASE64 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/".freeze
11
+ BASE94 = (
12
+ '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ' \
13
+ '[\]^_`abcdefghijklmnopqrstuvwxyz{|}~'
14
+ ).freeze
15
+
16
+ CROCKFORD_BASE32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ".freeze
17
+ ZBASE32 = "ybndrfg8ejkmcpqxot1uwisza345h769".freeze
18
+ BASE64_URL_SAFE = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_".freeze
19
+
20
+ ALL = [
21
+ BASE16, BASE32, BASE36, BASE52, BASE56, BASE58,
22
+ BASE62, BASE64, BASE94, CROCKFORD_BASE32, ZBASE32, BASE64_URL_SAFE
23
+ ].freeze
24
+ end
25
+ end
@@ -0,0 +1,11 @@
1
+ class Obfuskey
2
+ class Error < StandardError; end
3
+ class BitOverflowError < Error; end
4
+ class DuplicateError < Error; end
5
+ class KeyLengthError < Error; end
6
+ class MaximumValueError < Error; end
7
+ class MultiplierError < Error; end
8
+ class NegativeValueError < Error; end
9
+ class SchemaValidationError < Error; end
10
+ class UnknownKeyError < Error; end
11
+ end
@@ -0,0 +1,91 @@
1
+ class Obfuskey
2
+ module Math
3
+ module_function
4
+
5
+ def factor(n)
6
+ s = 0
7
+ d = n - 1
8
+ while d.even?
9
+ s += 1
10
+ d /= 2
11
+ end
12
+ [s, d]
13
+ end
14
+
15
+ def trial_division(n)
16
+ return false if n <= 1
17
+ return true if n == 2
18
+ return false if n.even?
19
+
20
+ i = 3
21
+ limit = Integer.sqrt(n)
22
+ while i <= limit
23
+ return false if (n % i).zero?
24
+ i += 2
25
+ end
26
+ true
27
+ end
28
+
29
+ def strong_pseudoprime(n, base = 2)
30
+ return false if n.even?
31
+ return false if n == 1
32
+
33
+ s, d = factor(n)
34
+ x = base.pow(d, n)
35
+ return true if x == 1 || x == n - 1
36
+
37
+ (s - 1).times do
38
+ x = x.pow(2, n)
39
+ return true if x == n - 1
40
+ return false if x == 1
41
+ end
42
+
43
+ false
44
+ end
45
+
46
+ def small_strong_pseudoprime(n)
47
+ [2, 13, 23, 1_662_803].all? { |base| strong_pseudoprime(n, base) }
48
+ end
49
+
50
+ def prime?(n)
51
+ return true if n == 2
52
+ return false if n < 2 || n.even?
53
+ return [3, 5, 7, 11, 13, 17].include?(n) if n.gcd(510_510) > 1
54
+ return trial_division(n) if n < 2_000_000
55
+
56
+ small_strong_pseudoprime(n)
57
+ end
58
+
59
+ def next_prime(n)
60
+ if n.bit_length > 512
61
+ raise MaximumValueError,
62
+ "For integers larger than 512-bit, a more advanced prime generator is required."
63
+ end
64
+
65
+ return 2 if n < 2
66
+ return [3, 5, 5][n - 2] if n < 5
67
+
68
+ gap = [
69
+ 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, 3,
70
+ 2, 1, 2, 1, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 2
71
+ ]
72
+
73
+ n += 1 + (n & 1)
74
+ n += gap[n % 30] if (n % 3).zero? || (n % 5).zero?
75
+ n += gap[n % 30] until prime?(n)
76
+ n
77
+ end
78
+
79
+ def mod_inv(base, mod)
80
+ g, x, _ = extended_gcd(base % mod, mod)
81
+ raise ArgumentError, "base and mod are not coprime" unless g == 1
82
+ x % mod
83
+ end
84
+
85
+ def extended_gcd(a, b)
86
+ return [a, 1, 0] if b.zero?
87
+ g, x1, y1 = extended_gcd(b, a % b)
88
+ [g, y1, x1 - (a / b) * y1]
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,130 @@
1
+ class Obfusbit
2
+ Error = Obfuskey::Error
3
+ MaximumValueError = Obfuskey::MaximumValueError
4
+ SchemaValidationError = Obfuskey::SchemaValidationError
5
+ BitOverflowError = Obfuskey::BitOverflowError
6
+
7
+ attr_reader :obfuskey
8
+
9
+ def initialize(schema, obfuskey: nil)
10
+ @schema = schema.is_a?(Obfuskey::ObfusbitSchema) ? schema : Obfuskey::ObfusbitSchema.new(schema)
11
+ @obfuskey = obfuskey
12
+ @total_bits = @schema.total_bits
13
+ @max_bits = @schema.max_bits
14
+
15
+ if @obfuskey && @max_bits > @obfuskey.maximum_value
16
+ raise MaximumValueError,
17
+ "The provided schema requires a maximum packed integer value of #{@max_bits} " \
18
+ "(which needs #{@total_bits} bits to represent), but the provided Obfuskey instance " \
19
+ "can only handle up to a maximum value of #{@obfuskey.maximum_value} " \
20
+ "(which covers #{@obfuskey.maximum_value.bit_length} bits)."
21
+ end
22
+ end
23
+
24
+ def total_bits
25
+ @total_bits
26
+ end
27
+
28
+ def max_bits
29
+ @max_bits
30
+ end
31
+
32
+ def schema
33
+ @schema.definition
34
+ end
35
+
36
+ def pack(values, obfuscate: false)
37
+ @schema.validate_values!(values)
38
+
39
+ normalized = values.each_with_object({}) { |(k, v), h| h[k.to_s] = v }
40
+ packed_int = 0
41
+
42
+ @schema.field_info.each do |name, info|
43
+ packed_int |= normalized[name] << info[:shift]
44
+ end
45
+
46
+ if obfuscate
47
+ raise ArgumentError, "An Obfuskey instance was not provided during initialization." unless @obfuskey
48
+ return @obfuskey.get_key(packed_int)
49
+ end
50
+
51
+ packed_int
52
+ end
53
+
54
+ def unpack(packed_data, obfuscated: false)
55
+ if obfuscated
56
+ raise ArgumentError, "An Obfuskey instance was not provided during initialization." unless @obfuskey
57
+ raise TypeError, "packed_data must be a String when obfuscated is true." unless packed_data.is_a?(String)
58
+ packed_int = @obfuskey.get_value(packed_data)
59
+ else
60
+ raise TypeError, "packed_data must be an Integer when obfuscated is false." unless packed_data.is_a?(Integer)
61
+ packed_int = packed_data
62
+ end
63
+
64
+ result = {}
65
+ @schema.field_info.each do |name, info|
66
+ mask = (1 << info[:bits]) - 1
67
+ result[name] = (packed_int >> info[:shift]) & mask
68
+ end
69
+ result
70
+ end
71
+
72
+ def pack_bytes(values, byteorder: :big)
73
+ packed_int = pack(values, obfuscate: false)
74
+ int_to_bytes(packed_int, byteorder)
75
+ end
76
+
77
+ def unpack_bytes(byte_data, byteorder: :big)
78
+ packed_int = bytes_to_int(byte_data, byteorder)
79
+ unpack(packed_int, obfuscated: false)
80
+ end
81
+
82
+ def to_s
83
+ key_repr = @obfuskey ? "obfuskey=#{@obfuskey}" : "no obfuskey"
84
+ "Obfusbit(schema=#{@schema}, #{key_repr})"
85
+ end
86
+ alias inspect to_s
87
+
88
+ private
89
+
90
+ def required_byte_length
91
+ (@total_bits + 7) / 8
92
+ end
93
+
94
+ def int_to_bytes(packed_int, byteorder)
95
+ unless packed_int >= 0 && packed_int <= @max_bits
96
+ raise ArgumentError,
97
+ "Packed integer #{packed_int} is out of range (0 to #{@max_bits}) " \
98
+ "for the schema's total bit capacity."
99
+ end
100
+
101
+ byte_length = required_byte_length
102
+ bytes = Array.new(byte_length)
103
+ value = packed_int
104
+ (byte_length - 1).downto(0) do |i|
105
+ bytes[i] = value & 0xff
106
+ value >>= 8
107
+ end
108
+
109
+ bytes = bytes.reverse if byteorder.to_sym == :little
110
+ bytes.pack("C*")
111
+ end
112
+
113
+ def bytes_to_int(byte_data, byteorder)
114
+ raise TypeError, "byte_data must be a String." unless byte_data.is_a?(String)
115
+
116
+ expected = required_byte_length
117
+ if byte_data.bytesize != expected
118
+ raise ArgumentError,
119
+ "Byte data length (#{byte_data.bytesize}) does not match expected length " \
120
+ "for this schema (#{expected} bytes based on #{@total_bits} bits)."
121
+ end
122
+
123
+ bytes = byte_data.bytes
124
+ bytes = bytes.reverse if byteorder.to_sym == :little
125
+
126
+ result = 0
127
+ bytes.each { |b| result = (result << 8) | b }
128
+ result
129
+ end
130
+ end
@@ -0,0 +1,112 @@
1
+ class Obfuskey
2
+ class ObfusbitSchema
3
+ attr_reader :definition, :total_bits, :max_bits, :field_names
4
+
5
+ def initialize(raw_schema)
6
+ validate_schema!(raw_schema)
7
+
8
+ @definition = raw_schema
9
+ @total_bits = raw_schema.sum { |item| item[:bits] || item["bits"] }
10
+ @max_bits = (1 << @total_bits) - 1
11
+ @field_info = calculate_field_info(raw_schema)
12
+ @field_names = raw_schema.map { |item| (item[:name] || item["name"]).to_s }.to_set
13
+ end
14
+
15
+ def field_info
16
+ # Return a deep copy so callers can't mutate internal state.
17
+ @field_info.each_with_object({}) { |(k, v), h| h[k] = v.dup }
18
+ end
19
+
20
+ def get_field_info(name)
21
+ info = @field_info[name.to_s]
22
+ raise ArgumentError, "Field '#{name}' not found in schema." unless info
23
+ info
24
+ end
25
+
26
+ def validate_values!(values)
27
+ input_names = values.keys.map(&:to_s).to_set
28
+
29
+ missing = @field_names - input_names
30
+ unless missing.empty?
31
+ raise ArgumentError,
32
+ "Required values for the following fields are missing: #{missing.sort.join(', ')}."
33
+ end
34
+
35
+ extra = input_names - @field_names
36
+ unless extra.empty?
37
+ raise ArgumentError,
38
+ "Unexpected fields provided in input values: #{extra.sort.join(', ')}."
39
+ end
40
+
41
+ values.each do |name, value|
42
+ info = get_field_info(name)
43
+ bits = info[:bits]
44
+ unless value.is_a?(Integer) && value >= 0 && value < (1 << bits)
45
+ raise BitOverflowError,
46
+ "Value '#{name}' (#{value}) exceeds its allocated #{bits} bits " \
47
+ "(maximum allowed: #{(1 << bits) - 1})."
48
+ end
49
+ end
50
+ end
51
+
52
+ def to_s
53
+ "ObfusbitSchema(total_bits=#{@total_bits}, fields=#{@definition.length})"
54
+ end
55
+ alias inspect to_s
56
+
57
+ private
58
+
59
+ def calculate_field_info(raw_schema)
60
+ info = {}
61
+ shift = 0
62
+ raw_schema.reverse_each do |item|
63
+ name = (item[:name] || item["name"]).to_s
64
+ bits = item[:bits] || item["bits"]
65
+ info[name] = { bits: bits, shift: shift }
66
+ shift += bits
67
+ end
68
+ info
69
+ end
70
+
71
+ def validate_schema!(schema)
72
+ raise SchemaValidationError, "Schema must be a list of dictionaries." unless schema.is_a?(Array)
73
+ raise SchemaValidationError, "Schema cannot be empty." if schema.empty?
74
+
75
+ seen = Set.new
76
+ schema.each_with_index do |item, i|
77
+ unless item.is_a?(Hash)
78
+ raise SchemaValidationError,
79
+ "Schema item at index #{i} must be a hash, got #{item.class.name}."
80
+ end
81
+
82
+ name = item[:name] || item["name"]
83
+ bits = item[:bits] || item["bits"]
84
+
85
+ raise SchemaValidationError, "Schema item at index #{i} is missing the 'name' key." if name.nil?
86
+ raise SchemaValidationError, "Schema item at index #{i} is missing the 'bits' key." if bits.nil?
87
+
88
+ unless name.is_a?(String) || name.is_a?(Symbol)
89
+ raise SchemaValidationError,
90
+ "Schema item (index #{i}): 'name' must be a string, got #{name.class.name}."
91
+ end
92
+
93
+ unless bits.is_a?(Integer)
94
+ raise SchemaValidationError,
95
+ "Schema item '#{name}' (index #{i}): 'bits' must be an integer, got #{bits.class.name}."
96
+ end
97
+
98
+ if bits <= 0
99
+ raise SchemaValidationError,
100
+ "Schema item '#{name}' (index #{i}): 'bits' must be a positive integer, got #{bits}."
101
+ end
102
+
103
+ name_str = name.to_s
104
+ if seen.include?(name_str)
105
+ raise SchemaValidationError,
106
+ "Schema contains duplicate name: '#{name_str}'. Names must be unique."
107
+ end
108
+ seen << name_str
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,85 @@
1
+ class Obfuskey
2
+ KEY_LENGTH = 6
3
+
4
+ attr_reader :alphabet, :key_length, :maximum_value
5
+
6
+ def initialize(alphabet, key_length: KEY_LENGTH, multiplier: nil)
7
+ if alphabet.chars.uniq.length != alphabet.length
8
+ raise DuplicateError, "The alphabet contains duplicate characters."
9
+ end
10
+
11
+ if !multiplier.nil? && (!multiplier.is_a?(Integer) || multiplier.even?)
12
+ raise MultiplierError, "The multiplier must be an odd integer."
13
+ end
14
+
15
+ @alphabet = alphabet
16
+ @key_length = key_length
17
+ @maximum_value = alphabet.length**key_length - 1
18
+ @multiplier = multiplier
19
+ @prime_multiplier = PRIME_MULTIPLIER
20
+ end
21
+
22
+ def multiplier
23
+ @multiplier ||= generate_multiplier
24
+ end
25
+
26
+ def set_prime_multiplier(prime_multiplier)
27
+ @prime_multiplier = prime_multiplier
28
+ @multiplier = nil
29
+ end
30
+
31
+ def get_key(value)
32
+ raise NegativeValueError, "The value must be greater than or equal to zero." if value < 0
33
+
34
+ if value > @maximum_value
35
+ raise MaximumValueError, "The maximum value possible is #{@maximum_value}"
36
+ end
37
+
38
+ return @alphabet[0] * @key_length if value.zero?
39
+
40
+ encoded = Utils.encode((value * multiplier) % (@maximum_value + 1), @alphabet)
41
+ encoded.rjust(@key_length, @alphabet[0])
42
+ end
43
+
44
+ def get_value(key)
45
+ unless key.chars.all? { |c| @alphabet.include?(c) }
46
+ raise UnknownKeyError,
47
+ "The key contains characters not found in the current alphabet."
48
+ end
49
+
50
+ if key.length != @key_length
51
+ raise KeyLengthError, "The key length does not match the set length."
52
+ end
53
+
54
+ return 0 if key == @alphabet[0] * @key_length
55
+
56
+ decoded = Utils.decode(key, @alphabet)
57
+ max_p1 = @maximum_value + 1
58
+ decoded * Math.mod_inv(multiplier, max_p1) % max_p1
59
+ end
60
+
61
+ def to_s
62
+ multiplier_info =
63
+ if @multiplier.nil?
64
+ ", multiplier=auto (prime_mult=#{@prime_multiplier})"
65
+ else
66
+ ", multiplier=#{@multiplier}"
67
+ end
68
+
69
+ display_alphabet =
70
+ if @alphabet.length > 20
71
+ "'#{@alphabet[0, 10]}...#{@alphabet[-5, 5]}'"
72
+ else
73
+ "'#{@alphabet}'"
74
+ end
75
+
76
+ "Obfuskey(alphabet=#{display_alphabet}, key_length=#{@key_length}#{multiplier_info})"
77
+ end
78
+ alias inspect to_s
79
+
80
+ private
81
+
82
+ def generate_multiplier
83
+ Utils.generate_prime(@alphabet, @key_length, @prime_multiplier)
84
+ end
85
+ end
@@ -0,0 +1,46 @@
1
+ class Obfuskey
2
+ PRIME_MULTIPLIER = 1.618033988749894848
3
+
4
+ module Utils
5
+ module_function
6
+
7
+ def decode(value, alphabet)
8
+ chars = value.chars
9
+ unless chars.all? { |c| alphabet.include?(c) }
10
+ raise UnknownKeyError,
11
+ "The value contains characters not found in the current alphabet."
12
+ end
13
+
14
+ return alphabet.index(value) if chars.length == 1
15
+
16
+ base = alphabet.length
17
+ result = 0
18
+ chars.each do |c|
19
+ result = result * base + alphabet.index(c)
20
+ end
21
+ result
22
+ end
23
+
24
+ def encode(value, alphabet)
25
+ raise NegativeValueError, "The value must be greater than or equal to zero." if value < 0
26
+
27
+ return alphabet[value] if value < alphabet.length
28
+
29
+ base = alphabet.length
30
+ key = +""
31
+ while value > 0
32
+ value, i = value.divmod(base)
33
+ key << alphabet[i]
34
+ end
35
+ key.reverse
36
+ end
37
+
38
+ def generate_prime(alphabet, key_length, prime_multiplier = PRIME_MULTIPLIER)
39
+ # Mirror Python's Decimal(float) conversion: Rational(float) preserves
40
+ # the exact binary representation of a Float, matching Python's behavior.
41
+ factor = prime_multiplier.is_a?(Float) ? Rational(prime_multiplier) : prime_multiplier
42
+ target = ((alphabet.length**key_length - 1) * factor).to_i
43
+ Math.next_prime(target)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,3 @@
1
+ class Obfuskey
2
+ VERSION = "0.1.0".freeze
3
+ end
data/lib/obfuskey.rb ADDED
@@ -0,0 +1,10 @@
1
+ require "set"
2
+
3
+ require "obfuskey/version"
4
+ require "obfuskey/errors"
5
+ require "obfuskey/alphabets"
6
+ require "obfuskey/math"
7
+ require "obfuskey/utils"
8
+ require "obfuskey/obfuskey"
9
+ require "obfuskey/obfusbit_schema"
10
+ require "obfuskey/obfusbit"
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: obfuskey
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Nathan Lucas
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rspec
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '3.12'
19
+ type: :development
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '3.12'
26
+ - !ruby/object:Gem::Dependency
27
+ name: rake
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '13.0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '13.0'
40
+ description: A Ruby port of obfuskey that produces deterministic, reversible keys
41
+ from integers using a custom alphabet. Cross-compatible with the Python, JavaScript,
42
+ and Rust implementations.
43
+ email:
44
+ - nathan@bnlucas.com
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - LICENSE
50
+ - README.md
51
+ - lib/obfuskey.rb
52
+ - lib/obfuskey/alphabets.rb
53
+ - lib/obfuskey/errors.rb
54
+ - lib/obfuskey/math.rb
55
+ - lib/obfuskey/obfusbit.rb
56
+ - lib/obfuskey/obfusbit_schema.rb
57
+ - lib/obfuskey/obfuskey.rb
58
+ - lib/obfuskey/utils.rb
59
+ - lib/obfuskey/version.rb
60
+ homepage: https://github.com/bnlucas/obfuskey-rb
61
+ licenses:
62
+ - MIT
63
+ metadata:
64
+ homepage_uri: https://github.com/bnlucas/obfuskey-rb
65
+ source_code_uri: https://github.com/bnlucas/obfuskey-rb
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: 2.7.0
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubygems_version: 3.6.9
81
+ specification_version: 4
82
+ summary: Generate obfuscated, fixed-length keys for integer values.
83
+ test_files: []