encinch 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/LICENSE.txt +22 -0
- data/README.md +65 -0
- data/encinch.gemspec +24 -0
- data/lib/cinch/plugins/encinch.rb +5 -0
- data/lib/cinch/plugins/encinch/encinch.rb +137 -0
- data/lib/cinch/plugins/encinch/encryption.rb +109 -0
- data/lib/cinch/plugins/encinch/extend.rb +47 -0
- data/lib/cinch/plugins/encinch/version.rb +6 -0
- metadata +81 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: edb2e2e7c096ffda8693dcd2c641689724862796
|
4
|
+
data.tar.gz: 2d8fcb47f7ee5ef3673503654d9cb6224f86d07f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 589cea1054cbf7f40b792fd6e52b9e51b4d69f81171f13819d204be8616c3576586f44a19a1590cc54a0e1efe80aaa393fe7a1dcf1717a4a8b190c18018ea36f
|
7
|
+
data.tar.gz: 5f640b06f13ca5e1c253ee0bae607762c22dec34e11b7201121a6f02e410cd7377c35825d49e41453448e035defc703bacbfc296377f886aa56ffa6badc97580
|
data/.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.DS_Store
|
6
|
+
.yardoc
|
7
|
+
Gemfile.lock
|
8
|
+
InstalledFiles
|
9
|
+
_yardoc
|
10
|
+
coverage
|
11
|
+
doc/
|
12
|
+
lib/bundler/man
|
13
|
+
pkg
|
14
|
+
rdoc
|
15
|
+
spec/reports
|
16
|
+
test/tmp
|
17
|
+
test/version_tmp
|
18
|
+
tmp
|
19
|
+
*.bundle
|
20
|
+
*.so
|
21
|
+
*.o
|
22
|
+
*.a
|
23
|
+
mkmf.log
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Jason Franz
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
|
2
|
+
# Encinch
|
3
|
+
|
4
|
+
Transparent blowfish encryption plugin for Cinch: An IRC Bot Building Framework
|
5
|
+
|
6
|
+
https://github.com/cinchrb/cinch
|
7
|
+
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
$[sudo] gem install encinch
|
12
|
+
|
13
|
+
## Example
|
14
|
+
```
|
15
|
+
require 'cinch'
|
16
|
+
require 'cinch/plugins/encinch'
|
17
|
+
|
18
|
+
|
19
|
+
bot = Cinch::Bot.new do
|
20
|
+
configure do |c|
|
21
|
+
c.nick = "EnCinch"
|
22
|
+
c.server = "irc.freenode.org"
|
23
|
+
c.port = 7000
|
24
|
+
c.ssl.use = true
|
25
|
+
c.channels = ["#cryptedchan", "#plaintext"]
|
26
|
+
|
27
|
+
c.plugins.plugins = [Cinch::Plugins::EnCinch] # optionally add more plugins
|
28
|
+
|
29
|
+
c.plugins.options[Cinch::Plugins::EnCinch] = {
|
30
|
+
:ignore => ["#ignorechannel", "ignoreperson"],
|
31
|
+
:encrypt => {
|
32
|
+
'#cryptedchan' => "myfishkey",
|
33
|
+
'cryptednick' => "notmyfishkey",
|
34
|
+
:default => "adefaultfishkey"
|
35
|
+
},
|
36
|
+
:uncrypted => ["#plaintext"]
|
37
|
+
}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
bot.start
|
42
|
+
```
|
43
|
+
|
44
|
+
## Commands
|
45
|
+
None, yet.
|
46
|
+
|
47
|
+
|
48
|
+
## Options
|
49
|
+
### :encrypt
|
50
|
+
A hash of encryption targets and their corresponding encryption keys. Targets should all be lowercase.
|
51
|
+
:default can provide a general purpose key, generally good for private communications with the bot.
|
52
|
+
The :encrypt option must be set, :default is optional.
|
53
|
+
|
54
|
+
### :uncrypted
|
55
|
+
An array of channels and individuals from which the bot will not encrypt messages. All entries must be lowercase. This option must be set. If there are no desired unencrypted targets set an empty array.
|
56
|
+
|
57
|
+
### :ignore
|
58
|
+
An array of channels and individuals (other bots?) to ignore encrypted messages. All entries must be lower case.
|
59
|
+
:ignore is optional.
|
60
|
+
|
61
|
+
## TODO
|
62
|
+
1) keyexchange
|
63
|
+
2) maybe store keys to a file?
|
64
|
+
3) add commands for updating, removing and adding keys and other settings?
|
65
|
+
4) user feedback... :-)
|
data/encinch.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'cinch/plugins/encinch/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "encinch"
|
8
|
+
spec.version = EnCinch::Version::VERSION
|
9
|
+
spec.authors = ["jfrazx"]
|
10
|
+
spec.email = ["staringblind@gmail.com"]
|
11
|
+
spec.summary = %q{Transparent blowfish encryption plugin for Cinch: An IRC Bot Building Framework}
|
12
|
+
spec.description = %q{}
|
13
|
+
spec.platform = Gem::Platform::RUBY
|
14
|
+
spec.homepage = "https://github.com/jfrazx/EnCinch"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0")
|
18
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_runtime_dependency "cinch", "~> 2.0"
|
23
|
+
spec.add_runtime_dependency "crypt", "~> 2.0"
|
24
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
|
2
|
+
module Cinch
|
3
|
+
module Plugins
|
4
|
+
class EnCinch
|
5
|
+
include Cinch::Plugin
|
6
|
+
|
7
|
+
def initialize(*)
|
8
|
+
super
|
9
|
+
|
10
|
+
# does Cinch have something to checked required options already???
|
11
|
+
raise MissingRequiredPluginOptions unless config[:encrypt]
|
12
|
+
raise MissingRequiredPluginOptions unless config[:uncrypted]
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
#
|
17
|
+
# All blowfish communication should be prefixed with +OK
|
18
|
+
# capture this to start the process
|
19
|
+
#
|
20
|
+
|
21
|
+
match(/\+OK (\S+)/, use_prefix: false, strip_colors: true, method: :capture)
|
22
|
+
|
23
|
+
def capture(m, message)
|
24
|
+
target = (m.channel ? m.channel.name : m.user.nick).downcase
|
25
|
+
message = strip(m, message)
|
26
|
+
|
27
|
+
return if config[:ignore].include?(target) rescue nil
|
28
|
+
@key = config[:encrypt][target] || config[:encrypt][:default] rescue nil
|
29
|
+
|
30
|
+
return unless @key
|
31
|
+
|
32
|
+
blowfish(@key)
|
33
|
+
|
34
|
+
decrypted = decrypt(message)
|
35
|
+
decrypted << '\u0001' if m.action?
|
36
|
+
|
37
|
+
raw = modify_raw(m.raw, decrypted)
|
38
|
+
|
39
|
+
dispatch(Message.new(raw, m.bot))
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# matching for key exchange
|
44
|
+
# NOT WORKING YET
|
45
|
+
# It appears Cinch does not emit a :notice event, unfortunate
|
46
|
+
#
|
47
|
+
|
48
|
+
match(/DH1080_INIT/, use_prefix: false, react_on: [:private, :notice], method: :key_exchange)
|
49
|
+
|
50
|
+
def key_exchange(m)
|
51
|
+
return if m.channel?
|
52
|
+
|
53
|
+
debug "captured key exchange event with key: #{m.message}"
|
54
|
+
|
55
|
+
#TODO -- everything
|
56
|
+
end
|
57
|
+
|
58
|
+
# not sure this is actually needed
|
59
|
+
def encrypt(message)
|
60
|
+
@fish.encrypt(message)
|
61
|
+
end
|
62
|
+
|
63
|
+
def decrypt(message)
|
64
|
+
@fish.decrypt(message)
|
65
|
+
end
|
66
|
+
|
67
|
+
def blowfish(key)
|
68
|
+
@fish = Cinch::Plugins::EnCinch::Encryption.new(key)
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def dispatch(msg)
|
74
|
+
events = [[:catchall]]
|
75
|
+
|
76
|
+
if ["PRIVMSG", "NOTICE"].include?(msg.command)
|
77
|
+
events << [:ctcp] if msg.ctcp?
|
78
|
+
|
79
|
+
if msg.channel?
|
80
|
+
events << [:channel]
|
81
|
+
else
|
82
|
+
events << [:private]
|
83
|
+
end
|
84
|
+
|
85
|
+
if msg.command == "PRIVMSG"
|
86
|
+
events << [:message]
|
87
|
+
else
|
88
|
+
events << [:notice]
|
89
|
+
end
|
90
|
+
|
91
|
+
if msg.action?
|
92
|
+
events << [:action]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
meth = "on_#{msg.command.downcase}"
|
97
|
+
__send__(meth, msg, events) if respond_to?(meth, true)
|
98
|
+
|
99
|
+
if msg.error?
|
100
|
+
events << [:error]
|
101
|
+
end
|
102
|
+
|
103
|
+
events << [msg.command.downcase.to_sym]
|
104
|
+
|
105
|
+
msg.events = events
|
106
|
+
|
107
|
+
#if its still encrypted for whatever reason we will not be processing it again
|
108
|
+
msg.events.each do |event, *args|
|
109
|
+
msg.bot.handlers.dispatch(event, msg, *args)
|
110
|
+
end unless encrypted?(msg.raw)
|
111
|
+
end
|
112
|
+
|
113
|
+
#
|
114
|
+
# TODO: strip ctcp? others?
|
115
|
+
#
|
116
|
+
|
117
|
+
def strip(m, data)
|
118
|
+
data.sub!(/\u0001$/, '') if m.action?
|
119
|
+
|
120
|
+
data
|
121
|
+
end
|
122
|
+
|
123
|
+
def encrypted?(data)
|
124
|
+
!!data.match(/\+OK \S+/)
|
125
|
+
end
|
126
|
+
|
127
|
+
#
|
128
|
+
# replace the encrypted message with the decrypted
|
129
|
+
#
|
130
|
+
|
131
|
+
def modify_raw(data, decrypted)
|
132
|
+
data.sub!(/\+OK \S+(\s+)?(\S+)?/, decrypted)
|
133
|
+
data
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
#
|
2
|
+
# much of this code was pilfered from Cinch's long dormant 'feature/fish' branch
|
3
|
+
# https://github.com/cinchrb/cinch/tree/feature/fish
|
4
|
+
# or from weechat's fish plugin
|
5
|
+
# https://github.com/weechat/scripts/blob/master/ruby/weefish.rb
|
6
|
+
#
|
7
|
+
|
8
|
+
module Cinch
|
9
|
+
module Plugins
|
10
|
+
class EnCinch
|
11
|
+
class Encryption
|
12
|
+
module Base64
|
13
|
+
|
14
|
+
Alphabet = "./0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".freeze
|
15
|
+
|
16
|
+
def self.encode(data)
|
17
|
+
res = String.new
|
18
|
+
data = data.dup.force_encoding("BINARY")
|
19
|
+
|
20
|
+
data.chars.each_slice(8) do |slice|
|
21
|
+
slice = slice.join
|
22
|
+
left, right = slice.unpack('L>L>')
|
23
|
+
6.times do
|
24
|
+
res << Alphabet[right & 0x3f]
|
25
|
+
right >>= 6
|
26
|
+
end
|
27
|
+
|
28
|
+
6.times do
|
29
|
+
res << Alphabet[left & 0x3f]
|
30
|
+
left >>= 6
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
return res
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.decode(data)
|
38
|
+
res = String.new
|
39
|
+
data = data.dup.force_encoding("BINARY")
|
40
|
+
data.chars.each_slice(12) do |slice|
|
41
|
+
slice = slice.join
|
42
|
+
left = right = 0
|
43
|
+
|
44
|
+
slice[0..5].each_char.with_index do |p, i|
|
45
|
+
right |= Alphabet.index(p) << (i * 6)
|
46
|
+
end
|
47
|
+
|
48
|
+
slice[6..11].each_char.with_index do |p, i|
|
49
|
+
left |= Alphabet.index(p) << (i * 6)
|
50
|
+
end
|
51
|
+
|
52
|
+
res << [left, right].pack('L>L>')
|
53
|
+
end
|
54
|
+
|
55
|
+
return res
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
def initialize(key)
|
61
|
+
@blowfish = Crypt::Blowfish.new(key)
|
62
|
+
end
|
63
|
+
|
64
|
+
def encrypt(text)
|
65
|
+
text = pad(text, 8)
|
66
|
+
result = String.new
|
67
|
+
|
68
|
+
num_block = text.length / 8
|
69
|
+
num_block.times do |n|
|
70
|
+
block = text[n*8..(n+1)*8-1]
|
71
|
+
enc = @blowfish.encrypt_block(block)
|
72
|
+
result += Base64.encode(enc)
|
73
|
+
end
|
74
|
+
|
75
|
+
return "+OK " << result
|
76
|
+
end
|
77
|
+
|
78
|
+
def decrypt(text)
|
79
|
+
return nil if not text.length % 12 == 0
|
80
|
+
|
81
|
+
result = String.new
|
82
|
+
|
83
|
+
num_block = (text.length / 12).to_i
|
84
|
+
num_block.times do |n|
|
85
|
+
block = Base64.decode( text[n*12..(n+1)*12-1] )
|
86
|
+
result += @blowfish.decrypt_block(block)
|
87
|
+
end
|
88
|
+
|
89
|
+
return result.gsub(/\0*$/, "")
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.generate
|
93
|
+
# generate a key for key exchange
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def pad(text, n=8)
|
99
|
+
pad_num = n - (text.length % n)
|
100
|
+
if pad_num > 0 and pad_num != n
|
101
|
+
pad_num.times { text += 0.chr }
|
102
|
+
end
|
103
|
+
|
104
|
+
return text
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
|
2
|
+
module Cinch
|
3
|
+
module Exceptions
|
4
|
+
|
5
|
+
# error to raise for missing required plugin options
|
6
|
+
class MissingRequiredPluginOptions < Generic
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
class IRC
|
12
|
+
|
13
|
+
# Send a message to the server.
|
14
|
+
# @param [String] msg
|
15
|
+
# @return [void]
|
16
|
+
def send(msg)
|
17
|
+
|
18
|
+
# TODO match and modify ACTION
|
19
|
+
if msg.match(/(PRIVMSG|NOTICE)/) && !msg.match(/\+OK (\S+)/)
|
20
|
+
|
21
|
+
verb, target, *message = msg.split
|
22
|
+
message = message.join(' ')[1..-1] rescue nil
|
23
|
+
|
24
|
+
# retrieve bot options
|
25
|
+
if (message && !message.empty?) && options = @bot.config.plugins.options[Cinch::Plugins::EnCinch]
|
26
|
+
|
27
|
+
# key exists
|
28
|
+
if key = (options[:encrypt][target.downcase] || options[:encrypt][:default])
|
29
|
+
|
30
|
+
# ignore if target is in the 'uncrypted' array
|
31
|
+
unless options[:uncrypted].include?(target.downcase)
|
32
|
+
|
33
|
+
fish = Cinch::Plugins::EnCinch::Encryption.new(key)
|
34
|
+
encrypted = fish.encrypt(message)
|
35
|
+
msg.sub!(message, encrypted)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
@queue.queue(msg)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: encinch
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- jfrazx
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-06-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: cinch
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: crypt
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.0'
|
41
|
+
description: ''
|
42
|
+
email:
|
43
|
+
- staringblind@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- ".gitignore"
|
49
|
+
- LICENSE.txt
|
50
|
+
- README.md
|
51
|
+
- encinch.gemspec
|
52
|
+
- lib/cinch/plugins/encinch.rb
|
53
|
+
- lib/cinch/plugins/encinch/encinch.rb
|
54
|
+
- lib/cinch/plugins/encinch/encryption.rb
|
55
|
+
- lib/cinch/plugins/encinch/extend.rb
|
56
|
+
- lib/cinch/plugins/encinch/version.rb
|
57
|
+
homepage: https://github.com/jfrazx/EnCinch
|
58
|
+
licenses:
|
59
|
+
- MIT
|
60
|
+
metadata: {}
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options: []
|
63
|
+
require_paths:
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
requirements: []
|
76
|
+
rubyforge_project:
|
77
|
+
rubygems_version: 2.2.2
|
78
|
+
signing_key:
|
79
|
+
specification_version: 4
|
80
|
+
summary: 'Transparent blowfish encryption plugin for Cinch: An IRC Bot Building Framework'
|
81
|
+
test_files: []
|