ipcrypt 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ffcebf3891f542221e9130fc8399490e5ec66a865f0ee64dc4bad819a9d4cd9d
4
+ data.tar.gz: ba53bcec1d4002cf843190fce26eddfec44a11e2f6f92281b3435395399960f0
5
+ SHA512:
6
+ metadata.gz: 935359db9f48e1280a9a0c1d29c270a92329649e0f353d3e022dbff79a43a76bf60858d1023ce12b21eb662dbb0787d8cdf656b72def425d86c9353006d2c2cc
7
+ data.tar.gz: 5b39e89d7f0193de858f9a944a34190eac1167d6d877c7af45a3b51eba4c0018c573f0c5ed91fabed218d8e93a808281f3913102ab462cbbf6cdde1a9dc5bdb5
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 Edwin Onuonga
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.
@@ -0,0 +1,129 @@
1
+ [![Ruby Version](https://img.shields.io/badge/ruby-~%3E%202.5-red.svg)](https://github.com/eonu/ipcrypt/blob/945450c32a145d7675b5d2b0c796708195d42388/ipcrypt.gemspec#L20)
2
+ [![Gem](https://img.shields.io/gem/v/ipcrypt.svg)](https://rubygems.org/gems/ipcrypt)
3
+ [![Build Status](https://travis-ci.org/eonu/ipcrypt.svg?branch=master)](https://travis-ci.org/eonu/ipcrypt)
4
+ [![License](https://img.shields.io/github/license/eonu/ipcrypt.svg)](https://github.com/eonu/ipcrypt/blob/master/LICENSE)
5
+
6
+ # IPCrypt
7
+
8
+ Ruby implementation of the format-preserving IPCrypt encryption algorithm for IPv4 addresses.
9
+
10
+ ---
11
+
12
+ IPCrypt is a format-preserving cipher for IPv4 addresses - that is, a cipher that accepts an IPv4 address for encryption, and generates a new decryptable IPv4 address.
13
+
14
+ The cipher was developed by Jean-Philippe Aumasson, initially in the form of a [python implementation](https://github.com/veorq/ipcrypt) which is the reference for this Ruby implementation.
15
+
16
+ ## Features
17
+
18
+ This gem provides:
19
+
20
+ - A CLI tool (`ipcrypt`) for the encryption/decryption of IPv4 addresses stored in CSV files
21
+ - A Ruby interface in the form of a module (`IPCrypt::IP`) for the encryption/decryption of IPv4 addresses of the class `String` within Ruby applications
22
+
23
+ ## Installation
24
+
25
+ Install the `ipcrypt` gem:
26
+
27
+ ```bash
28
+ $ gem install ipcrypt
29
+ ```
30
+
31
+ ### Installation for CLI usage
32
+
33
+ The CLI should be available to use after the gem has been installed:
34
+
35
+ ```bash
36
+ $ ipcrypt
37
+ Commands:
38
+ ipcrypt d [CSV] [COLUMN] # Decrypt IPv4 addresses from a CSV file
39
+ ipcrypt e [CSV] [COLUMN] # Encrypt IPv4 addresses from a CSV file
40
+
41
+ Options:
42
+ -k, --key=KEY # 16-byte key
43
+ ```
44
+
45
+ ### Installation for usage within Ruby applications
46
+
47
+ 1. Add the gem to your application's Gemfile:
48
+
49
+ ```ruby
50
+ gem 'ipcrypt'
51
+ ```
52
+
53
+ 2. Execute the following command:
54
+
55
+ ```bash
56
+ $ bundle install
57
+ ```
58
+
59
+ ## CLI Usage
60
+
61
+ ```bash
62
+ $ cat test.csv
63
+ id,firstname,lastname,ip_address,country
64
+ 1,a,b,127.0.0.1,c
65
+ 2,d,e,0.0.0.0,f
66
+ 3,g,h,255.255.255.255,i
67
+ 4,j,k,192.168.2.1,l
68
+
69
+ $ ipcrypt e test.csv ip_address -k '16-byte-key-123!' > encrypted.csv
70
+
71
+ $ cat encrypted.csv
72
+ id,firstname,lastname,ip_address,country
73
+ 1,a,b,94.99.154.180,c
74
+ 2,d,e,34.112.126.36,f
75
+ 3,g,h,6.156.93.249,i
76
+ 4,j,k,41.85.161.64,l
77
+
78
+ $ ipcrypt d encrypted.csv ip_address -k '16-byte-key-123!'
79
+ id,firstname,lastname,ip_address,country
80
+ 1,a,b,127.0.0.1,c
81
+ 2,d,e,0.0.0.0,f
82
+ 3,g,h,255.255.255.255,i
83
+ 4,j,k,192.168.2.1,l
84
+ ```
85
+
86
+ ## Usage within Ruby applications
87
+
88
+ The `IPCrypt::IP` is an interface for instantiating an `IPCrypt::Engine` - this class performs the task of encryption and decryption.
89
+
90
+ A random 16-byte key will be generated and stored as the `@default_key` instance variable - this can be retrieved with the `#default_key` attribute reader. This default key is used as the encryption key if none is specified as an argument for the `#encrypt` instance method.
91
+
92
+ ### Using a default key
93
+
94
+ ```ruby
95
+ encrypter = IPCrypt::IP['94.175.013.122', '73.155.92.01']
96
+ => #<IPCrypt::Engine:0x00007ff9f904fc68 @default_key="\x95\xC6\rM\xE5\xFAKE\xAD\x16\x80\x9A\xF6\xA9\v\x8B", @ips=["94.175.013.122", "73.155.92.01"]>
97
+
98
+ encrypted = encrypter.encrypt
99
+ => ["221.69.213.73", "80.1.170.94"]
100
+
101
+ decrypter = IPCrypt::IP[encrypted]
102
+ => #<IPCrypt::Engine:0x00007ff9f91618e0 @default_key="\xF8\x00\x8Cdj\xFF\xE9\x82\xE9\v\x85e\xF6\x7F 8", @ips=["221.69.213.73", "80.1.170.94"]>
103
+
104
+ decrypted = decrypter.decrypt encrypter.default_key
105
+ => ["94.175.13.122", "73.155.92.1"]
106
+ ```
107
+
108
+ ### Using a set key
109
+
110
+ ```ruby
111
+ key = '16-byte-key-123!'
112
+ => "16-byte-key-123"
113
+
114
+ encrypter = IPCrypt::IP['94.175.013.122', '73.155.92.01']
115
+ => #<IPCrypt::Engine:0x00007fe851049db8 @default_key="\xCC\xE0j\x13s\xB9B+\xEF'\xC8\xFC\xD4\xA5\xFCW", @ips=["94.175.013.122", "73.155.92.01"]>
116
+
117
+ encrypted = encrypter.encrypt key
118
+ => ["74.248.51.155", "132.12.16.129"]
119
+
120
+ decrypter = IPCrypt::IP[encrypted]
121
+ => #<IPCrypt::Engine:0x00007fe85117b1c8 @default_key="\xFA\x9C\x1E\xEE\xA7\xE3\xA1\xFD(9Q\x94\x11\xA4\x90\x19", @ips=["74.248.51.155", "132.12.16.129"]>
122
+
123
+ decrypted = decrypter.decrypt key
124
+ => ["94.175.13.122", "73.155.92.1"]
125
+ ```
126
+
127
+ ## Contributing
128
+
129
+ Bug reports and pull requests are welcome on GitHub at https://github.com/eonu/ipcrypt.
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+ task :default => [:spec]
4
+
5
+ desc "Run the specs"
6
+ RSpec::Core::RakeTask.new(:spec)
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require 'ipcrypt/cli'
3
+ IPCrypt::CLI.start
@@ -0,0 +1,28 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'ipcrypt/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'ipcrypt'
7
+ spec.version = IPCrypt::VERSION
8
+ spec.authors = ['Edwin Onuonga']
9
+ spec.email = %w[edwinonuonga@gmail.com]
10
+ spec.license = 'MIT'
11
+ spec.summary = %q{Ruby implementation of the format-preserving IPCrypt encryption algorithm for IPv4 addresses.}
12
+ spec.homepage = 'https://github.com/eonu/ipcrypt'
13
+ spec.metadata = {'source_code_uri' => 'https://github.com/eonu/ipcrypt/'}
14
+
15
+ spec.files = Dir.glob(File.join('lib', '**', '*'), File::FNM_DOTMATCH) + %w[Gemfile LICENSE README.md Rakefile ipcrypt.gemspec bin/ipcrypt]
16
+ spec.bindir = 'bin'
17
+ spec.executables = 'ipcrypt'
18
+ spec.require_paths = ['lib']
19
+
20
+ spec.required_ruby_version = "~> 2.5"
21
+
22
+ spec.add_development_dependency 'bundler', '~> 1.16'
23
+ spec.add_development_dependency 'rake', '~> 10.0'
24
+ spec.add_development_dependency 'rspec', '~> 3.8'
25
+ spec.add_development_dependency 'rspec-collection_matchers', '~> 1.1'
26
+ spec.add_development_dependency 'faker', '~> 1.9'
27
+ spec.add_runtime_dependency 'thor', '~> 0.20'
28
+ end
@@ -0,0 +1,3 @@
1
+ require 'ipcrypt/version'
2
+ require 'ipcrypt/ip'
3
+ require 'ipcrypt/engine'
@@ -0,0 +1,42 @@
1
+ require 'ipcrypt/engine'
2
+ require 'thor'
3
+ require 'csv'
4
+ module IPCrypt
5
+ class CLI < Thor
6
+ class_option :key, aliases: '-k', type: :string, :desc => '16-byte key', required: true
7
+
8
+ %w[encrypt decrypt].each do |crypt|
9
+ desc "#{crypt} [CSV] [COLUMN]", "#{crypt.capitalize} IPv4 addresses from a CSV file"
10
+ define_method crypt do |file, column|
11
+ csv = CSV.read(file, headers: true)
12
+ puts csv.headers * ?,
13
+
14
+ _crypted = Engine.new(*csv[column]).send(crypt, options[:key])
15
+
16
+ CSV.foreach(file, headers: true).with_index do |row, i|
17
+ puts csv.headers.map {|h| h == column ? _crypted[i] : row[h]} * ?,
18
+ end
19
+ end
20
+ end
21
+
22
+ def self.help(shell, subcommand = false)
23
+ list = printable_commands(true, subcommand)
24
+ Thor::Util.thor_classes_in(self).each do |klass|
25
+ list += klass.printable_commands(false)
26
+ end
27
+ list.sort! { |a, b| a[0] <=> b[0] }
28
+ list.reject! { |e| /.*help.*/.match? e.first }
29
+ list.map! {|c| c.map {|s| s.gsub('decrypt', ?d).gsub('encrypt', ?e)} }
30
+
31
+ if defined?(@package_name) && @package_name
32
+ shell.say "#{@package_name} commands:"
33
+ else
34
+ shell.say "Commands:"
35
+ end
36
+
37
+ shell.print_table(list, :indent => 2, :truncate => true)
38
+ shell.say
39
+ class_options_help(shell)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,120 @@
1
+ require 'ipcrypt/exceptions'
2
+ require 'securerandom'
3
+ module IPCrypt
4
+ class Engine
5
+ attr_reader :default_key
6
+ IPv4 = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
7
+
8
+ def initialize(*ips)
9
+ @default_key = SecureRandom.random_bytes(16)
10
+ @ips = ips.flatten.map do |ip|
11
+ raise InvalidIPv4Error.new(ip) unless IPv4.match? ip
12
+ ip
13
+ end
14
+ end
15
+
16
+ def encrypt(key = @default_key)
17
+ raise InvalidKeyTypeError.new(key, key.class) unless key.is_a? String
18
+
19
+ bytes = key.bytesize
20
+ raise InvalidKeyBytesError.new(key, bytes) unless bytes == 16
21
+
22
+ encrypted = @ips.map do |ip|
23
+ k = key.chars.map {|c| c.unpack('<C').first }
24
+ state = ip.split(?.).map &:to_i
25
+
26
+ state = xor4 state, k[0...4]
27
+ state = permute_fwd state
28
+ state = xor4 state, k[4...8]
29
+ state = permute_fwd state
30
+ state = xor4 state, k[8...12]
31
+ state = permute_fwd state
32
+ state = xor4 state, k[12...16]
33
+
34
+ state * ?.
35
+ end
36
+
37
+ encrypted.size == 1 ? encrypted.first : encrypted
38
+ end
39
+
40
+ def decrypt(key)
41
+ raise InvalidKeyTypeError.new(key, key.class) unless key.is_a? String
42
+
43
+ bytes = key.bytesize
44
+ raise InvalidKeyBytesError.new(key, bytes) unless bytes == 16
45
+
46
+ decrypted = @ips.map do |ip|
47
+ k = key.chars.map {|c| c.unpack('<C').first }
48
+ state = ip.split(?.).map &:to_i
49
+
50
+ state = xor4 state, k[12...16]
51
+ state = permute_bwd state
52
+ state = xor4 state, k[8...12]
53
+ state = permute_bwd state
54
+ state = xor4 state, k[4...8]
55
+ state = permute_bwd state
56
+ state = xor4 state, k[0...4]
57
+
58
+ state * ?.
59
+ end
60
+
61
+ decrypted.size == 1 ? decrypted.first : decrypted
62
+ end
63
+
64
+ private
65
+
66
+ def rotl(b, r)
67
+ ((b << r) & 0xff) | (b >> (8 - r))
68
+ end
69
+
70
+ def xor4(x, y)
71
+ (0..3).map {|i| (x[i] ^ y[i]) & 0xff }
72
+ end
73
+
74
+ def permute_fwd(state)
75
+ b0, b1, b2, b3 = state
76
+ b0 += b1
77
+ b2 += b3
78
+ b0 &= 0xff
79
+ b2 &= 0xff
80
+ b1 = rotl b1, 2
81
+ b3 = rotl b3, 5
82
+ b1 ^= b0
83
+ b3 ^= b2
84
+ b0 = rotl b0, 4
85
+ b0 += b3
86
+ b2 += b1
87
+ b0 &= 0xff
88
+ b2 &= 0xff
89
+ b1 = rotl b1, 3
90
+ b3 = rotl b3, 7
91
+ b1 ^= b2
92
+ b3 ^= b0
93
+ b2 = rotl b2, 4
94
+ [b0, b1, b2, b3]
95
+ end
96
+
97
+ def permute_bwd(state)
98
+ b0, b1, b2, b3 = state
99
+ b2 = rotl b2, 4
100
+ b1 ^= b2
101
+ b3 ^= b0
102
+ b1 = rotl b1, 5
103
+ b3 = rotl b3, 1
104
+ b0 -= b3
105
+ b2 -= b1
106
+ b0 &= 0xff
107
+ b2 &= 0xff
108
+ b0 = rotl b0, 4
109
+ b1 ^= b0
110
+ b3 ^= b2
111
+ b1 = rotl b1, 6
112
+ b3 = rotl b3, 3
113
+ b0 -= b1
114
+ b2 -= b3
115
+ b0 &= 0xff
116
+ b2 &= 0xff
117
+ [b0, b1, b2, b3]
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,17 @@
1
+ module IPCrypt
2
+ class InvalidIPv4Error < Exception
3
+ def initialize(ip)
4
+ super "Invalid IPv4 address: #{ip}"
5
+ end
6
+ end
7
+ class InvalidKeyTypeError < Exception
8
+ def initialize(key, type)
9
+ super "Expected key '#{key}' to be a String, got #{type}"
10
+ end
11
+ end
12
+ class InvalidKeyBytesError < Exception
13
+ def initialize(key, bytes)
14
+ super "Expected key '#{key}' to be 16-byte, got #{bytes} byte#{bytes == 1 ? '' : ?s}"
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,6 @@
1
+ require 'ipcrypt/engine'
2
+ module IPCrypt
3
+ module IP
4
+ def self.[](*ips) Engine.new(*ips) end
5
+ end
6
+ end
@@ -0,0 +1,3 @@
1
+ module IPCrypt
2
+ VERSION = "1.0.0"
3
+ end
metadata ADDED
@@ -0,0 +1,143 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ipcrypt
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Edwin Onuonga
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-12-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.8'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.8'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec-collection_matchers
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.1'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: faker
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.9'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.9'
83
+ - !ruby/object:Gem::Dependency
84
+ name: thor
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.20'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.20'
97
+ description:
98
+ email:
99
+ - edwinonuonga@gmail.com
100
+ executables:
101
+ - ipcrypt
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - Gemfile
106
+ - LICENSE
107
+ - README.md
108
+ - Rakefile
109
+ - bin/ipcrypt
110
+ - ipcrypt.gemspec
111
+ - lib/ipcrypt.rb
112
+ - lib/ipcrypt/cli.rb
113
+ - lib/ipcrypt/engine.rb
114
+ - lib/ipcrypt/exceptions.rb
115
+ - lib/ipcrypt/ip.rb
116
+ - lib/ipcrypt/version.rb
117
+ homepage: https://github.com/eonu/ipcrypt
118
+ licenses:
119
+ - MIT
120
+ metadata:
121
+ source_code_uri: https://github.com/eonu/ipcrypt/
122
+ post_install_message:
123
+ rdoc_options: []
124
+ require_paths:
125
+ - lib
126
+ required_ruby_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: '2.5'
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ requirements: []
137
+ rubyforge_project:
138
+ rubygems_version: 2.7.6
139
+ signing_key:
140
+ specification_version: 4
141
+ summary: Ruby implementation of the format-preserving IPCrypt encryption algorithm
142
+ for IPv4 addresses.
143
+ test_files: []