gfc64 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +71 -10
- data/lib/gfc64/active_record.rb +39 -0
- data/lib/gfc64.rb +3 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9d7c2a4aaef604ecbe6d4c432f001bae3122383ab3c7e0d3e588db43b075da7a
|
4
|
+
data.tar.gz: 11d79c4899212011f01b93b2d1b86c7663b84cdb73184817aa997fb6988a1f05
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dab37bcb93e364b9dcd98d1004e2b0c1bc7c62ad8d44650d3aedc80093564449972e8e1e1c550083baee464cd02bf7a6241a9cfa921fc5b771fbbe9c0c81c185
|
7
|
+
data.tar.gz: dba7ac573a35f604bc7ebb4d82e82d76becd983799318d5e1eda6a6cb867b5367adfbde0fa9dabead075299bd5e5d7c1611b61bf86559cacec4f641a337b589d
|
data/README.md
CHANGED
@@ -1,20 +1,39 @@
|
|
1
1
|
# GFC64
|
2
2
|
|
3
|
-
|
4
|
-
integers into... 64-bit integers!
|
3
|
+
Encrypt 64-bit integers into other 64-bit integers without collisions.
|
5
4
|
|
6
|
-
|
7
|
-
|
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,
|
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`
|
11
|
+
`/customers/1`
|
14
12
|
|
15
|
-
`/customers/2`
|
13
|
+
`/customers/2`
|
16
14
|
|
17
|
-
|
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
|
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
|
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-
|
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.
|
38
|
+
version: 2.4.0
|
38
39
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
39
40
|
requirements:
|
40
41
|
- - ">="
|