gfc64 0.0.2 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f93c96115de165e7cec9824e893e7254b6612c5d254c19d19c14899c7aa68ef1
4
- data.tar.gz: 00fc60e41ebe8a82bd7de95386aaa097df8a468ecb13e1ad98c7a4b5a311f59c
3
+ metadata.gz: 9d7c2a4aaef604ecbe6d4c432f001bae3122383ab3c7e0d3e588db43b075da7a
4
+ data.tar.gz: 11d79c4899212011f01b93b2d1b86c7663b84cdb73184817aa997fb6988a1f05
5
5
  SHA512:
6
- metadata.gz: 2b7b53943801d158fbc1e44c2a9bd6038aa4445047ebfc7de941f3dc583bb55120e9353dbea6d12de4253d1bc67f88c44e4a1c2326a549f4dd13a6a0842e2041
7
- data.tar.gz: 6674b80a4457530858aa15b57d89b421431e81313066356d95013292257b160936efde74b27349f410d5cc002dc64bf5dfe9fb00f08af830d72c5dfb2268a617
6
+ metadata.gz: dab37bcb93e364b9dcd98d1004e2b0c1bc7c62ad8d44650d3aedc80093564449972e8e1e1c550083baee464cd02bf7a6241a9cfa921fc5b771fbbe9c0c81c185
7
+ data.tar.gz: dba7ac573a35f604bc7ebb4d82e82d76becd983799318d5e1eda6a6cb867b5367adfbde0fa9dabead075299bd5e5d7c1611b61bf86559cacec4f641a337b589d
data/README.md CHANGED
@@ -1,20 +1,39 @@
1
1
  # GFC64
2
2
 
3
- Format-preserving Generalized Feistel Cipher for 64-bit integers. Encrypts 64-bit
4
- integers into... 64-bit integers!
3
+ Encrypt 64-bit integers into other 64-bit integers without collisions.
5
4
 
6
- Very useful for if you have sequential 64-bit integer database primary keys that
7
- you want to expose in an app, but you don't want to leak count information
8
- (e.g. `/customers/1`, `/customers/2`, ...), and you don't want to add another
9
- column to store something auxiliary like a UUID.
5
+ Useful for hiding database auto-incrementing primary keys without needing
6
+ to store additional data (like a UUID field).
10
7
 
11
- For example, with this gem, routes like
8
+ For example, say you have a web app where routes are stored like `/customers/:id`
9
+ so that you have exposed URL paths like this:
12
10
 
13
- `/customers/1` can become something like `/customers/4552956331295818987`, and
11
+ `/customers/1`
14
12
 
15
- `/customers/2` can become something like `/customers/3833777695217202560`
13
+ `/customers/2`
16
14
 
17
- ## Usage
15
+ With this gem, you can (at view-time) encrypt the above IDs into different IDs
16
+ so that you get these paths instead, without storing any additional data:
17
+
18
+ `/customers/4552956331295818987` (the backend decrypts this into `/customers/1`)
19
+
20
+ `/customers/3833777695217202560` (the backend decrypts this into `/customers/2`)
21
+
22
+ All while keeping your auto-incrementing, sequential IDs in your database and
23
+ getting all the benefits of a standard database index.
24
+
25
+ The encryption is achieved by implementing a format-preserving
26
+ [Generalized Feistel Cipher][paper].
27
+
28
+ ## Installation
29
+
30
+ Add to your `Gemfile`:
31
+
32
+ ```ruby
33
+ gem "gfc64"
34
+ ```
35
+
36
+ ## General Usage
18
37
 
19
38
  ```ruby
20
39
  key = SecureRandom.hex(32) # => "ffb5e3600fc27924f97dc055440403b10ce97160261f2a87eee576584cf942e5"
@@ -25,6 +44,48 @@ gfc.encrypt(2) # => 3833777695217202560
25
44
  gfc.decrypt(3833777695217202560) # => 2
26
45
  ```
27
46
 
47
+ ## Rails Usage
48
+
49
+ For Rails, there's an ActiveRecord mixin which adds `#gfc_id` and
50
+ `#to_param` methods and a `::find_gfc` class method. By setting `#to_param`,
51
+ resource path helpers like `customer_path(@customer)` automatically use the
52
+ GFC encrypted ID.
53
+
54
+ Example usage:
55
+
56
+ ```ruby
57
+ # app/models/customer.rb
58
+
59
+ class Customer < ApplicationRecord
60
+ include GFC64::ActiveRecord[GFC64.new(ENV['GFC_KEY'])]
61
+ # or the argument can be a proc/lambda if you need late binding:
62
+ # include GFC64::ActiveRecord[-> { GFC64.new(ENV['GFC_KEY']) }]
63
+ end
64
+ ```
65
+
66
+ For retrieval, use `::find_gfc`:
67
+
68
+ ```ruby
69
+ # app/controllers/customers_controller.rb
70
+
71
+ class CustomersController < ApplicationController
72
+ def show
73
+ @customer = Customer.find_gfc(params[:id])
74
+ end
75
+ end
76
+ ```
77
+
78
+ ## Potential drawbacks
79
+
80
+ Ruby's dynamic typing means we're just passing bare Integers around, so it's
81
+ possible to make a mistake where you write something like `Model.find(gfc_id)`
82
+ and return a completely unexpected record because you forgot to decrypt the ID.
83
+
84
+ With a type system it would be trivial to prevent these errors. However, the
85
+ space of 64-bit integers is very large, and encrypted IDs tend to occupy numbers
86
+ much higher than most web apps will ever reach, so more than likely this sort of
87
+ error would be discovered quickly as queries fail to find anything.
88
+
28
89
  ## Disclaimer
29
90
 
30
91
  This code has not been vetted by a security audit or a professional
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ class GFC64
4
+ module ActiveRecord
5
+ def self.[](gfc)
6
+ Module.new do
7
+ include ActiveRecord
8
+
9
+ define_singleton_method :included do |klass|
10
+ klass.extend(ClassMethods)
11
+ klass.send(:include, InstanceMethods)
12
+ klass.f_gfc = gfc
13
+ end
14
+ end
15
+ end
16
+
17
+ module ClassMethods
18
+ attr_accessor :f_gfc
19
+
20
+ def gfc
21
+ @gfc ||= f_gfc.respond_to?(:call) ? f_gfc.call : f_gfc
22
+ end
23
+
24
+ def find_gfc(gfc_id)
25
+ find(gfc.decrypt(gfc_id))
26
+ end
27
+ end
28
+
29
+ module InstanceMethods
30
+ def gfc_id
31
+ id && self.class.gfc.encrypt(id)
32
+ end
33
+
34
+ def to_param
35
+ gfc_id.to_s
36
+ end
37
+ end
38
+ end
39
+ end
data/lib/gfc64.rb CHANGED
@@ -4,7 +4,7 @@ require "openssl"
4
4
  require "securerandom"
5
5
 
6
6
  class GFC64
7
- VERSION = "0.0.2".freeze
7
+ VERSION = "0.1.0".freeze
8
8
 
9
9
  attr_reader :rounds, :key, :block_size
10
10
 
@@ -55,3 +55,5 @@ class GFC64
55
55
  OpenSSL::Digest::SHA256.digest(key + [round].pack('L'))[0...16] # Truncate to 128 bits for AES key
56
56
  end
57
57
  end
58
+
59
+ require "gfc64/active_record"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gfc64
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Abe Voelker
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-11-03 00:00:00.000000000 Z
11
+ date: 2023-11-04 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Hides sequential primary key counts without resorting to UUIDs or GUIDs.
14
14
  email: abe@abevoelker.com
@@ -18,6 +18,7 @@ extra_rdoc_files: []
18
18
  files:
19
19
  - README.md
20
20
  - lib/gfc64.rb
21
+ - lib/gfc64/active_record.rb
21
22
  homepage: https://github.com/abevoelker/gfc64
22
23
  licenses:
23
24
  - MIT
@@ -34,7 +35,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
34
35
  requirements:
35
36
  - - ">="
36
37
  - !ruby/object:Gem::Version
37
- version: 2.1.0
38
+ version: 2.4.0
38
39
  required_rubygems_version: !ruby/object:Gem::Requirement
39
40
  requirements:
40
41
  - - ">="