encrypted 0.0.1
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 +7 -0
- data/.gitignore +14 -0
- data/.idea/.name +1 -0
- data/.idea/.rakeTasks +7 -0
- data/.idea/compiler.xml +23 -0
- data/.idea/copyright/profiles_settings.xml +3 -0
- data/.idea/dictionaries/asishbhattarai.xml +3 -0
- data/.idea/encodings.xml +5 -0
- data/.idea/misc.xml +8 -0
- data/.idea/modules.xml +9 -0
- data/.idea/scopes/scope_settings.xml +5 -0
- data/.idea/vcs.xml +7 -0
- data/.idea/workspace.xml +647 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +24 -0
- data/README.md +31 -0
- data/Rakefile +2 -0
- data/encrypted.gemspec +23 -0
- data/encrypted.iml +25 -0
- data/lib/encrypted.rb +6 -0
- data/lib/encrypted/bytestream.rb +71 -0
- data/lib/encrypted/cbc.rb +73 -0
- data/lib/encrypted/rijndael.rb +163 -0
- data/lib/encrypted/rijndael/core.rb +430 -0
- data/lib/encrypted/version.rb +3 -0
- metadata +96 -0
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Copyright 2005-2011 Jim Driscoll.
|
|
2
|
+
All rights reserved.
|
|
3
|
+
|
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
|
5
|
+
modification, are permitted provided that the following conditions
|
|
6
|
+
are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright
|
|
9
|
+
notice, this list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright
|
|
12
|
+
notice, this list of conditions and the following disclaimer in the
|
|
13
|
+
documentation and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
16
|
+
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
17
|
+
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
18
|
+
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
19
|
+
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
20
|
+
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
21
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
22
|
+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
23
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
24
|
+
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Encrypted
|
|
2
|
+
|
|
3
|
+
TODO: Write a gem description
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add this line to your application's Gemfile:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
gem 'encrypted'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
And then execute:
|
|
14
|
+
|
|
15
|
+
$ bundle
|
|
16
|
+
|
|
17
|
+
Or install it yourself as:
|
|
18
|
+
|
|
19
|
+
$ gem install encrypted
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
TODO: Write usage instructions here
|
|
24
|
+
|
|
25
|
+
## Contributing
|
|
26
|
+
|
|
27
|
+
1. Fork it ( https://github.com/getaasciesh/encrypted/fork )
|
|
28
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
29
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
30
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
31
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/encrypted.gemspec
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'encrypted/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "encrypted"
|
|
8
|
+
spec.version = Encrypted::VERSION
|
|
9
|
+
spec.authors = ["asish bhattarai"]
|
|
10
|
+
spec.email = ["getaasciesh@hotmail.com"]
|
|
11
|
+
spec.summary = %q{Rijndael encryption with cbc.}
|
|
12
|
+
spec.description = %q{Rijndael encryption with cbc. More Coming soon.}
|
|
13
|
+
spec.homepage = "https://github.com/getaasciesh/encrypted"
|
|
14
|
+
spec.license = "LICENCE.txt"
|
|
15
|
+
|
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
|
+
spec.require_paths = ["lib"]
|
|
20
|
+
|
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
|
23
|
+
end
|
data/encrypted.iml
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<module type="RUBY_MODULE" version="4">
|
|
3
|
+
<component name="CompassSettings">
|
|
4
|
+
<option name="compassSupportEnabled" value="true" />
|
|
5
|
+
</component>
|
|
6
|
+
<component name="FacetManager">
|
|
7
|
+
<facet type="gem" name="Ruby Gem">
|
|
8
|
+
<configuration>
|
|
9
|
+
<option name="GEM_APP_ROOT_PATH" value="$MODULE_DIR$" />
|
|
10
|
+
<option name="GEM_APP_TEST_PATH" value="$MODULE_DIR$/test" />
|
|
11
|
+
<option name="GEM_APP_LIB_PATH" value="$MODULE_DIR$/lib" />
|
|
12
|
+
</configuration>
|
|
13
|
+
</facet>
|
|
14
|
+
</component>
|
|
15
|
+
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
|
16
|
+
<exclude-output />
|
|
17
|
+
<content url="file://$MODULE_DIR$">
|
|
18
|
+
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
|
19
|
+
</content>
|
|
20
|
+
<orderEntry type="jdk" jdkName="ruby-2.0.0-p451" jdkType="RUBY_SDK" />
|
|
21
|
+
<orderEntry type="sourceFolder" forTests="false" />
|
|
22
|
+
<orderEntry type="library" scope="PROVIDED" name="rake (v10.3.2, ruby-2.0.0-p451) [gem]" level="application" />
|
|
23
|
+
</component>
|
|
24
|
+
</module>
|
|
25
|
+
|
data/lib/encrypted.rb
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
module Encrypted
|
|
2
|
+
class ByteStream < String
|
|
3
|
+
=begin rdoc
|
|
4
|
+
A subclass of String with a single purpose: to provide the ^ (XOR) operator, for encryption purposes.
|
|
5
|
+
=end
|
|
6
|
+
|
|
7
|
+
=begin rdoc
|
|
8
|
+
Returned values are still of class Encrypted::ByteStream, and are of the same length as the longer value. Note that this means that:
|
|
9
|
+
a=Encrypted::ByteStream.new("a")
|
|
10
|
+
bb=Encrypted::ByteStream.new("b")
|
|
11
|
+
a^bb^bb
|
|
12
|
+
|
|
13
|
+
...does not equal "a" but rather "a\000", so this should be used with caution except where you have equal-size strings in mind.
|
|
14
|
+
=end
|
|
15
|
+
def ^(string)
|
|
16
|
+
max_length=if(self.length > string.length)
|
|
17
|
+
self.length
|
|
18
|
+
else
|
|
19
|
+
string.length
|
|
20
|
+
end
|
|
21
|
+
my_bytes=self.unpack("C*")
|
|
22
|
+
other_bytes=string.unpack("C*")
|
|
23
|
+
out_bytes=Array.new
|
|
24
|
+
0.upto(max_length-1) do
|
|
25
|
+
|i|
|
|
26
|
+
out_bytes[i]=(my_bytes[i]||0)^(other_bytes[i]||0)
|
|
27
|
+
end
|
|
28
|
+
self.class.new(out_bytes.pack("C*"))
|
|
29
|
+
end
|
|
30
|
+
def +(string)
|
|
31
|
+
my_dwords=self.unpack("N*")
|
|
32
|
+
other_dwords=string.unpack("N*")
|
|
33
|
+
max_length=if(my_dwords.length > other_dwords.length)
|
|
34
|
+
my_dwords.length
|
|
35
|
+
else
|
|
36
|
+
other_dwords.length
|
|
37
|
+
end
|
|
38
|
+
out_dwords=Array.new
|
|
39
|
+
0.upto(max_length-1) do
|
|
40
|
+
|i|
|
|
41
|
+
out_dwords[i]=(my_dwords[i]||0)+(other_dwords[i]||0)&0xffffffff
|
|
42
|
+
end
|
|
43
|
+
self.class.new(out_dwords.pack("N*"))
|
|
44
|
+
end
|
|
45
|
+
Use_getbyte = "".respond_to?(:getbyte)
|
|
46
|
+
def byte_at(position, new_value=nil)
|
|
47
|
+
if(new_value)
|
|
48
|
+
self[position, 1]=[new_value].pack("C")
|
|
49
|
+
elsif(Use_getbyte)
|
|
50
|
+
self.getbyte(position)
|
|
51
|
+
else
|
|
52
|
+
self.slice(position)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
@@strict_mode = false
|
|
56
|
+
def self.strict_mode=(new_mode)
|
|
57
|
+
@@strict_mode=new_mode
|
|
58
|
+
end
|
|
59
|
+
def [](anything, whatever=nil)
|
|
60
|
+
if(whatever)
|
|
61
|
+
super(anything, whatever)
|
|
62
|
+
elsif(not anything.is_a? Numeric)
|
|
63
|
+
super(anything)
|
|
64
|
+
elsif(@@strict_mode)
|
|
65
|
+
raise "Ambiguous, you must use #byte_at instead"
|
|
66
|
+
else
|
|
67
|
+
STDERR.puts "Ambiguous usage of [], please use #byte_at"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
unless(defined? Encrypted::ByteStream)
|
|
2
|
+
require "bytestream"
|
|
3
|
+
end
|
|
4
|
+
module Encrypted
|
|
5
|
+
class CBC
|
|
6
|
+
# YARV (1.9) compat
|
|
7
|
+
Use_getbyte = "".respond_to?(:getbyte)
|
|
8
|
+
|
|
9
|
+
def CBC.pad_pkcs5(string, to_length) #:nodoc:
|
|
10
|
+
diff= to_length - (string.length % to_length)
|
|
11
|
+
string+=[diff].pack("C") * diff
|
|
12
|
+
return string
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def CBC.unpad_pkcs5(string) #:nodoc:
|
|
16
|
+
return unless string.length > 0
|
|
17
|
+
|
|
18
|
+
if(Use_getbyte) # 1.9 returns a string from []
|
|
19
|
+
pad_len = string.getbyte(-1)
|
|
20
|
+
else
|
|
21
|
+
pad_len = string[-1]
|
|
22
|
+
end
|
|
23
|
+
unless(string.slice!(-pad_len .. -1) == [pad_len].pack("C") * pad_len)
|
|
24
|
+
raise "Unpad failure: trailing junk found"
|
|
25
|
+
end
|
|
26
|
+
return string
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def initialize(cipher)
|
|
30
|
+
@cipher=cipher
|
|
31
|
+
end
|
|
32
|
+
def encrypt(iv, plaintext)
|
|
33
|
+
block_size=iv.length
|
|
34
|
+
|
|
35
|
+
last_block_e=Encrypted::ByteStream.new(iv)
|
|
36
|
+
|
|
37
|
+
plaintext=CBC.pad_pkcs5(plaintext, iv.length)
|
|
38
|
+
r_data="-" * plaintext.length
|
|
39
|
+
|
|
40
|
+
j=0
|
|
41
|
+
pt_l = plaintext.length
|
|
42
|
+
while(j<pt_l)
|
|
43
|
+
last_block_e[0,block_size]=@cipher.encrypt(last_block_e^plaintext[j, block_size])
|
|
44
|
+
r_data[j, block_size]=last_block_e
|
|
45
|
+
j+=block_size
|
|
46
|
+
end
|
|
47
|
+
return r_data
|
|
48
|
+
end
|
|
49
|
+
def decrypt(iv, ciphertext)
|
|
50
|
+
block_size=iv.length
|
|
51
|
+
|
|
52
|
+
last_block_e=Encrypted::ByteStream.new(iv)
|
|
53
|
+
|
|
54
|
+
unless(ciphertext.length % block_size==0)
|
|
55
|
+
raise "Bad IV: doesn't match ciphertext length"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
r_data="-" * ciphertext.length
|
|
59
|
+
j=0
|
|
60
|
+
ct_l = ciphertext.length
|
|
61
|
+
current_block = "-" * block_size
|
|
62
|
+
while(j<ct_l)
|
|
63
|
+
current_block=ciphertext[j, block_size]
|
|
64
|
+
|
|
65
|
+
r_data[j, block_size]=last_block_e^@cipher.decrypt(current_block)
|
|
66
|
+
last_block_e[0,block_size]=current_block
|
|
67
|
+
j+=block_size
|
|
68
|
+
end
|
|
69
|
+
r_data=CBC.unpad_pkcs5(r_data)
|
|
70
|
+
return r_data
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
unless(defined? Encrypted::ByteStream)
|
|
2
|
+
require 'bytestream'
|
|
3
|
+
Encrypted::ByteStream.strict_mode = true
|
|
4
|
+
end
|
|
5
|
+
# This is to help testing
|
|
6
|
+
unless(defined? Encrypted::Rijndael::Core)
|
|
7
|
+
require "rijndael/core"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module Encrypted
|
|
11
|
+
=begin rdoc
|
|
12
|
+
Encrypted::Rijndael allows you to encrypt single blocks of data using the encrypt() and decrypt() methods
|
|
13
|
+
below.
|
|
14
|
+
|
|
15
|
+
You probably want to use some kind of CBC module with this.
|
|
16
|
+
=end
|
|
17
|
+
class Rijndael
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@@valid_blocksizes_bytes=[16, 24, 32]
|
|
22
|
+
@@valid_keysizes_bytes=[16, 24, 32]
|
|
23
|
+
|
|
24
|
+
=begin rdoc
|
|
25
|
+
The new() function here takes only one argument: the key to use, as a String (or similar). Valid lengths
|
|
26
|
+
are 16, 24 or 32 bytes, and you should ensure that this value is sufficiently random. Most people will
|
|
27
|
+
choose 16-byte (128-bit) keys, but a longer key will take longer to crack if security is of unusually
|
|
28
|
+
high importance for you.
|
|
29
|
+
=end
|
|
30
|
+
def initialize(new_key)
|
|
31
|
+
self.key = new_key
|
|
32
|
+
@current_block_length = nil # This makes it easier to adjust in #block=
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
attr_reader :key
|
|
36
|
+
|
|
37
|
+
# If you want to, you can assign a new key to an existing object.
|
|
38
|
+
def key=(new_key)
|
|
39
|
+
raise "Invalid key length: #{new_key.length}" unless(self.class.key_sizes_supported.find {|size| size==new_key.length})
|
|
40
|
+
@key = new_key
|
|
41
|
+
@key_words=@key.length/4
|
|
42
|
+
@expanded_key = nil
|
|
43
|
+
@round_count = nil
|
|
44
|
+
end
|
|
45
|
+
def expand_key #:nodoc:
|
|
46
|
+
return @expanded_key if(@expanded_key)
|
|
47
|
+
@expanded_key=(@key_words>6)? Core.expand_key_gt6(key, @block_words):
|
|
48
|
+
Core.expand_key_le6(key, @block_words)
|
|
49
|
+
return @expanded_key
|
|
50
|
+
end
|
|
51
|
+
protected :expand_key
|
|
52
|
+
|
|
53
|
+
attr_reader :block
|
|
54
|
+
def block=(new_block) #:nodoc:
|
|
55
|
+
if(new_block.length != @current_block_length) then
|
|
56
|
+
raise "Invalid block size: #{new_block.length}" unless(block_sizes_supported.find { |size| size==new_block.length })
|
|
57
|
+
@current_block_length = new_block.length
|
|
58
|
+
@block_words = @current_block_length / 4
|
|
59
|
+
@expanded_key = nil
|
|
60
|
+
@round_count = nil
|
|
61
|
+
end
|
|
62
|
+
@block = new_block
|
|
63
|
+
end
|
|
64
|
+
protected :block=, :block, :key
|
|
65
|
+
|
|
66
|
+
# If you want to probe for supported block sizes, by all means use this method. It'll raise
|
|
67
|
+
# if the value isn't supported.
|
|
68
|
+
#
|
|
69
|
+
# Don't use this: #block_sizes_supported is better.
|
|
70
|
+
def blocksize=(block_size_bytes)
|
|
71
|
+
self.block = "\x00" * block_size_bytes
|
|
72
|
+
self
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# This lets you know how big a block is currently being used.
|
|
76
|
+
# There's probably no point using this.
|
|
77
|
+
def blocksize
|
|
78
|
+
return @block_words*4
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Provides a list of block sizes (bytes) which are supported
|
|
82
|
+
def self.block_sizes_supported
|
|
83
|
+
@@valid_blocksizes_bytes
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Provides a list of key sizes (bytes) which are supported
|
|
87
|
+
def self.key_sizes_supported
|
|
88
|
+
@@valid_keysizes_bytes
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# This just calls the class' .block_sizes_supported method for you.
|
|
92
|
+
def block_sizes_supported
|
|
93
|
+
self.class.block_sizes_supported
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def round_count #:nodoc:
|
|
98
|
+
return @round_count if @round_count
|
|
99
|
+
@round_count = Core.round_count(@block_words, @key_words)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
protected :round_count
|
|
104
|
+
|
|
105
|
+
=begin rdoc
|
|
106
|
+
Your main entry point. You must provide an input string of a valid length - if not, it'll +raise+.
|
|
107
|
+
Valid lengths are 16, 24 or 32 bytes, and it will pick the block size based on the length of the input.
|
|
108
|
+
|
|
109
|
+
The output is a Encrypted::ByteStream object, which is to say more-or-less a String.
|
|
110
|
+
=end
|
|
111
|
+
def encrypt(plaintext)
|
|
112
|
+
self.block = plaintext
|
|
113
|
+
|
|
114
|
+
rounds=round_count
|
|
115
|
+
expanded_key=expand_key
|
|
116
|
+
|
|
117
|
+
blockl_b=@block_words*4
|
|
118
|
+
#puts "m #{block.length}"
|
|
119
|
+
tmp_block=Core.round0(block, expanded_key[0])
|
|
120
|
+
tmp_block = Core.roundn_times(tmp_block, expanded_key, rounds, :forward)
|
|
121
|
+
return Core.roundl(tmp_block, expanded_key[rounds])
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
=begin rdoc
|
|
125
|
+
Your other main entry point. You must provide an input string of a valid length - if not, it'll +raise+.
|
|
126
|
+
Valid lengths are 16, 24 or 32 bytes, and it will pick the block size based on the length of the input.
|
|
127
|
+
Of course, if the string to decrypt is of invalid length then you've got other problems...
|
|
128
|
+
|
|
129
|
+
The output is a Encrypted::ByteStream object, which is to say more-or-less a String.
|
|
130
|
+
=end
|
|
131
|
+
def decrypt(ciphertext)
|
|
132
|
+
self.block = ciphertext
|
|
133
|
+
rounds=round_count
|
|
134
|
+
expanded_key=expand_key
|
|
135
|
+
|
|
136
|
+
blockl_b=@block_words*4
|
|
137
|
+
tmp_block=Core.inv_roundl(block, expanded_key[rounds])
|
|
138
|
+
tmp_block = Core.roundn_times(tmp_block, expanded_key, rounds, :reverse)
|
|
139
|
+
decrypted=Core.round0(tmp_block, expanded_key[0])
|
|
140
|
+
#p "decrypted: #{decrypted}" if $VERBOSE
|
|
141
|
+
return decrypted
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
=begin rdoc
|
|
146
|
+
This is exactly the same as Encrypted::Rijndael except that the only allowed block size is 128-bit (16 bytes
|
|
147
|
+
), which affects possible IV (for CBC and other block-chaining algorithms) and plaintext block lengths.
|
|
148
|
+
|
|
149
|
+
Given the effort that went into standardising on AES, you may well want to use this instead of
|
|
150
|
+
Encrypted::Rijndael for encryption if you're interoperating with another party. Of course, you *can* safely
|
|
151
|
+
use Encrypted::Rijndael for decryption in that circumstance.
|
|
152
|
+
|
|
153
|
+
The spec for this is in an US government standards document named FIPS-197. Google for it.
|
|
154
|
+
=end
|
|
155
|
+
class AES < Rijndael
|
|
156
|
+
AES_BLOCKSIZE_BYTES=16
|
|
157
|
+
|
|
158
|
+
# Only one block size is supported for real AES: 16 bytes.
|
|
159
|
+
def self.block_sizes_supported
|
|
160
|
+
[AES_BLOCKSIZE_BYTES]
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
class Encrypted
|
|
2
|
+
class Rijndael
|
|
3
|
+
class Core
|
|
4
|
+
@@rounds_by_block_size={
|
|
5
|
+
4=>10,
|
|
6
|
+
6=>12,
|
|
7
|
+
8=>14
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
def self.round_count(block_words, key_words) #:nodoc:
|
|
11
|
+
biggest_words=if(block_words > key_words)
|
|
12
|
+
block_words
|
|
13
|
+
else
|
|
14
|
+
key_words
|
|
15
|
+
end
|
|
16
|
+
@@rounds_by_block_size[biggest_words]
|
|
17
|
+
end
|
|
18
|
+
def self.round_constants(block_words, key_words) #:nodoc:
|
|
19
|
+
@@round_constants ||= {}
|
|
20
|
+
@@round_constants[block_words] ||= {}
|
|
21
|
+
unless(@@round_constants[block_words][key_words]) then
|
|
22
|
+
temp_v=1
|
|
23
|
+
p_round_constant=[0,1].map {|i| [i, 0, 0, 0].pack("C*")}
|
|
24
|
+
|
|
25
|
+
p_round_constant+=
|
|
26
|
+
(2 .. (block_words * (round_count(block_words, key_words) + 1)/key_words).to_i).to_a.map {
|
|
27
|
+
#0x1000000<<($_-1)
|
|
28
|
+
[(temp_v=Core.dot(02,temp_v)),0,0,0].pack("C*")
|
|
29
|
+
}
|
|
30
|
+
@@round_constants[block_words][key_words] = p_round_constant
|
|
31
|
+
end
|
|
32
|
+
@@round_constants[block_words][key_words]
|
|
33
|
+
end
|
|
34
|
+
def self.expand_key_le6(key, block_words) #:nodoc
|
|
35
|
+
# For short (128-bit, 192-bit) keys this is used to expand the key to blocklen*(rounds+1) bits
|
|
36
|
+
|
|
37
|
+
#expanded_key=key;
|
|
38
|
+
ek_words=key.unpack("N*").map {|number| Encrypted::ByteStream.new([number].pack("N"))}
|
|
39
|
+
|
|
40
|
+
key_words = key.length / 4
|
|
41
|
+
p_round_constant = round_constants(block_words, key_words)
|
|
42
|
+
|
|
43
|
+
rounds=round_count(block_words, key_words)
|
|
44
|
+
|
|
45
|
+
(key_words .. block_words * (rounds + 1)-1).each do
|
|
46
|
+
|i|
|
|
47
|
+
|
|
48
|
+
p_temp=ek_words[i-1]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
if(i % key_words == 0)
|
|
52
|
+
|
|
53
|
+
t_byte=p_temp.byte_at(0)
|
|
54
|
+
p_temp[0 .. 2]=p_temp[1 .. 3]
|
|
55
|
+
p_temp.byte_at(3, t_byte)
|
|
56
|
+
|
|
57
|
+
# tr would be great here again.
|
|
58
|
+
p_temp=Encrypted::ByteStream.new(Core.sbox_block(p_temp))
|
|
59
|
+
p_temp^=p_round_constant[(i/key_words).to_i]
|
|
60
|
+
end
|
|
61
|
+
ek_words[i]=p_temp^ek_words[i-key_words]
|
|
62
|
+
i+=1
|
|
63
|
+
end
|
|
64
|
+
#puts ek_words.to_s
|
|
65
|
+
expanded_key=Array(rounds+1)
|
|
66
|
+
(0 .. rounds).each do
|
|
67
|
+
|round|
|
|
68
|
+
expanded_key[round]=Encrypted::ByteStream.new(ek_words[round*block_words, block_words].join(""))
|
|
69
|
+
end
|
|
70
|
+
return expanded_key;
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def self.expand_key_gt6(key, block_words) #:nodoc:
|
|
74
|
+
# For long (256-bit) keys this is used to expand the key to blocklen*(rounds+1) bits
|
|
75
|
+
|
|
76
|
+
#expanded_key=key
|
|
77
|
+
ek_words=key.unpack("N*").map {|number| Encrypted::ByteStream.new([number].pack("N"))}
|
|
78
|
+
|
|
79
|
+
key_words = key.length / 4
|
|
80
|
+
p_round_constant = round_constants(block_words, key_words)
|
|
81
|
+
|
|
82
|
+
rounds=round_count(block_words, key_words)
|
|
83
|
+
|
|
84
|
+
(key_words .. block_words * (rounds + 1)-1).each do
|
|
85
|
+
|i|
|
|
86
|
+
|
|
87
|
+
p_temp=ek_words[i-1]
|
|
88
|
+
if(i % key_words == 0)
|
|
89
|
+
|
|
90
|
+
t_byte=p_temp.byte_at(0)
|
|
91
|
+
p_temp[0 .. 2]=p_temp[1 .. 3]
|
|
92
|
+
p_temp.byte_at(3, t_byte)
|
|
93
|
+
|
|
94
|
+
# tr would be great here again.
|
|
95
|
+
p_temp=Encrypted::ByteStream.new(Core.sbox_block(p_temp))
|
|
96
|
+
p_temp^=p_round_constant[(i/key_words).to_i]
|
|
97
|
+
|
|
98
|
+
elsif(i % key_words == 4)
|
|
99
|
+
p_temp=Core.sbox_block(p_temp)
|
|
100
|
+
end
|
|
101
|
+
ek_words[i]=ek_words[i-key_words]^p_temp
|
|
102
|
+
end
|
|
103
|
+
expanded_key=Array(rounds+1)
|
|
104
|
+
(0 .. rounds).each do
|
|
105
|
+
|round|
|
|
106
|
+
expanded_key[round]=Encrypted::ByteStream.new(ek_words[round*block_words, block_words].join(""))
|
|
107
|
+
end
|
|
108
|
+
return expanded_key;
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def self.roundn_times(block, expanded_key, rounds, direction) #:nodoc:
|
|
112
|
+
case(direction)
|
|
113
|
+
when :forward then
|
|
114
|
+
(1 .. rounds-1).each do
|
|
115
|
+
|current_round|
|
|
116
|
+
block=Core.roundn(block, expanded_key[current_round])
|
|
117
|
+
end
|
|
118
|
+
when :reverse then
|
|
119
|
+
(1 .. rounds-1).to_a.reverse.each do
|
|
120
|
+
|current_round|
|
|
121
|
+
block=Core.inv_roundn(block, expanded_key[current_round])
|
|
122
|
+
end
|
|
123
|
+
else
|
|
124
|
+
raise "Unsupported round direction"
|
|
125
|
+
end
|
|
126
|
+
block
|
|
127
|
+
end
|
|
128
|
+
def self.roundn(input, round_key) #:nodoc:
|
|
129
|
+
block_words = input.length / 4
|
|
130
|
+
row_len=block_words;
|
|
131
|
+
|
|
132
|
+
input=sbox_block(input)
|
|
133
|
+
input=shift_rows(input)
|
|
134
|
+
# Tune this - jim
|
|
135
|
+
input=mix_column(input)
|
|
136
|
+
|
|
137
|
+
return round0(input, round_key)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def self.inv_roundn(input, round_key) #:nodoc:
|
|
141
|
+
block_words = input.length / 4
|
|
142
|
+
|
|
143
|
+
input=round0(input, round_key)
|
|
144
|
+
row_len=block_words
|
|
145
|
+
input=inv_mix_column(input)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
input=inv_shift_rows(input)
|
|
149
|
+
# convert to use tr for the s-box ?
|
|
150
|
+
input=inv_sbox_block(input)
|
|
151
|
+
|
|
152
|
+
return input
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def self.roundl(input, round_key) #:nodoc:
|
|
156
|
+
# convert to use tr for the s-box
|
|
157
|
+
|
|
158
|
+
input=sbox_block(input)
|
|
159
|
+
input=shift_rows(input)
|
|
160
|
+
return round0(input, round_key)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def self.inv_roundl(input, round_key) #:nodoc:
|
|
164
|
+
# convert to use tr for the s-box
|
|
165
|
+
input=round0(input, round_key)
|
|
166
|
+
input=inv_sbox_block(input)
|
|
167
|
+
input=inv_shift_rows(input)
|
|
168
|
+
#input=bytes_n.pack("C*")
|
|
169
|
+
return input
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def self.round0(input, round_key) #:nodoc:
|
|
174
|
+
return round_key^input;
|
|
175
|
+
end
|
|
176
|
+
def self.make_shiftrow_map #:nodoc:
|
|
177
|
+
shift_for_block_len={
|
|
178
|
+
4=>[0,1,2,3],
|
|
179
|
+
6=>[0,1,2,3],
|
|
180
|
+
8=>[0,1,3,4],
|
|
181
|
+
}
|
|
182
|
+
@@inv_shiftrow_map=(0 .. 0xff).map {Array.new}
|
|
183
|
+
@@shiftrow_map=(0 .. 0xff).map {Array.new}
|
|
184
|
+
shift_for_block_len.keys.each do
|
|
185
|
+
|block_len|
|
|
186
|
+
row_len=block_len;
|
|
187
|
+
state_b=(0 .. (row_len*4)-1).to_a;
|
|
188
|
+
col_len=4;
|
|
189
|
+
c=shift_for_block_len[block_len];
|
|
190
|
+
(0 .. c.length-1).each do
|
|
191
|
+
|row_n|
|
|
192
|
+
# Grab the lossage first
|
|
193
|
+
next unless c[row_n] > 0;
|
|
194
|
+
d1=Array.new
|
|
195
|
+
d2=Array.new
|
|
196
|
+
(row_len-c[row_n] .. row_len-1).map {|col| row_n+col_len*col}.each do
|
|
197
|
+
|offset|
|
|
198
|
+
d1+=state_b[offset,1]
|
|
199
|
+
end
|
|
200
|
+
(0 .. row_len-c[row_n]-1).map {|col| row_n+col_len*col}.each do
|
|
201
|
+
|offset|
|
|
202
|
+
d2+=state_b[offset,1]
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
(0 .. row_len-1).map {|col| row_n+col_len*col}.each do
|
|
206
|
+
|offset|
|
|
207
|
+
state_b[offset]=d1.shift||d2.shift
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
@@inv_shiftrow_map[block_len]=state_b;
|
|
211
|
+
(0 .. state_b.length-1).each do
|
|
212
|
+
|offset|
|
|
213
|
+
@@shiftrow_map[block_len][state_b[offset]]=offset;
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
make_shiftrow_map
|
|
219
|
+
|
|
220
|
+
def self.shift_rows(state_b) #:nodoc:
|
|
221
|
+
row_len=state_b.length/4
|
|
222
|
+
|
|
223
|
+
state_o=@@shiftrow_map[row_len].map do
|
|
224
|
+
|offset|
|
|
225
|
+
state_b.byte_at(offset)
|
|
226
|
+
end
|
|
227
|
+
return Encrypted::ByteStream.new(state_o.pack("C*"))
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def self.inv_shift_rows(state_b) #:nodoc:
|
|
231
|
+
col_len=4;
|
|
232
|
+
row_len=state_b.length/4;
|
|
233
|
+
|
|
234
|
+
state_o=@@inv_shiftrow_map[row_len].map do
|
|
235
|
+
|offset|
|
|
236
|
+
state_b.byte_at(offset)
|
|
237
|
+
end
|
|
238
|
+
return Encrypted::ByteStream.new(state_o.pack("C*"))
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
POLYNOMIAL_SPACE=0x11b
|
|
243
|
+
COLUMN_SIZE=4
|
|
244
|
+
|
|
245
|
+
def self.sbox_block(input)
|
|
246
|
+
return Encrypted::ByteStream.new(input.unpack("C*").map do
|
|
247
|
+
|byte|
|
|
248
|
+
@@sbox[byte]
|
|
249
|
+
end.pack("C*"))
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def self.inv_sbox_block(input)
|
|
253
|
+
return Encrypted::ByteStream.new(input.unpack("C*").map do
|
|
254
|
+
|byte|
|
|
255
|
+
@@inv_sbox[byte]
|
|
256
|
+
end.pack("C*"))
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def self.mix_column(col)
|
|
260
|
+
block_words=col.length/COLUMN_SIZE
|
|
261
|
+
r_col=Array.new
|
|
262
|
+
(0 .. (block_words-1)).each {
|
|
263
|
+
|current_word|
|
|
264
|
+
r_col+=[
|
|
265
|
+
(@@dot_cache[02][col.byte_at((current_word*4)+0)] ^
|
|
266
|
+
@@dot_cache[03][col.byte_at((current_word*4)+1)] ^
|
|
267
|
+
col.byte_at((current_word*4)+2) ^
|
|
268
|
+
col.byte_at((current_word*4)+3) ),
|
|
269
|
+
( col.byte_at((current_word*4)+0) ^
|
|
270
|
+
@@dot_cache[02][col.byte_at((current_word*4)+1)] ^
|
|
271
|
+
@@dot_cache[03][col.byte_at((current_word*4)+2)] ^
|
|
272
|
+
col.byte_at((current_word*4)+3) ),
|
|
273
|
+
( col.byte_at((current_word*4)+0) ^
|
|
274
|
+
col.byte_at((current_word*4)+1) ^
|
|
275
|
+
@@dot_cache[02][col.byte_at((current_word*4)+2)] ^
|
|
276
|
+
@@dot_cache[03][col.byte_at((current_word*4)+3)]),
|
|
277
|
+
(@@dot_cache[03][col.byte_at((current_word*4)+0)] ^
|
|
278
|
+
col.byte_at((current_word*4)+1) ^
|
|
279
|
+
col.byte_at((current_word*4)+2) ^
|
|
280
|
+
@@dot_cache[02][col.byte_at((current_word*4)+3)])]
|
|
281
|
+
}
|
|
282
|
+
return Encrypted::ByteStream.new(r_col.pack("C*"))
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
# The inverse of the above
|
|
286
|
+
|
|
287
|
+
def self.inv_mix_column(col)
|
|
288
|
+
block_words=col.length/COLUMN_SIZE
|
|
289
|
+
r_col=Array.new
|
|
290
|
+
(0 .. (block_words-1)).each { |current_block|
|
|
291
|
+
r_col+=[
|
|
292
|
+
(@@dot_cache[0x0e][col.byte_at((current_block*4)+0)] ^
|
|
293
|
+
@@dot_cache[0x0b][col.byte_at((current_block*4)+1)] ^
|
|
294
|
+
@@dot_cache[0x0d][col.byte_at((current_block*4)+2)] ^
|
|
295
|
+
@@dot_cache[0x09][col.byte_at((current_block*4)+3)]),
|
|
296
|
+
(@@dot_cache[0x09][col.byte_at((current_block*4)+0)] ^
|
|
297
|
+
@@dot_cache[0x0e][col.byte_at((current_block*4)+1)] ^
|
|
298
|
+
@@dot_cache[0x0b][col.byte_at((current_block*4)+2)] ^
|
|
299
|
+
@@dot_cache[0x0d][col.byte_at((current_block*4)+3)]),
|
|
300
|
+
(@@dot_cache[0x0d][col.byte_at((current_block*4)+0)] ^
|
|
301
|
+
@@dot_cache[0x09][col.byte_at((current_block*4)+1)] ^
|
|
302
|
+
@@dot_cache[0x0e][col.byte_at((current_block*4)+2)] ^
|
|
303
|
+
@@dot_cache[0x0b][col.byte_at((current_block*4)+3)]),
|
|
304
|
+
(@@dot_cache[0x0b][col.byte_at((current_block*4)+0)] ^
|
|
305
|
+
@@dot_cache[0x0d][col.byte_at((current_block*4)+1)] ^
|
|
306
|
+
@@dot_cache[0x09][col.byte_at((current_block*4)+2)] ^
|
|
307
|
+
@@dot_cache[0x0e][col.byte_at((current_block*4)+3)])
|
|
308
|
+
]}
|
|
309
|
+
return Encrypted::ByteStream.new(r_col.pack("C*"))
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def self.xtime(a)
|
|
313
|
+
a*=2
|
|
314
|
+
if( a & 0x100 > 0 )
|
|
315
|
+
a^=0x1b
|
|
316
|
+
end
|
|
317
|
+
a&=0xff
|
|
318
|
+
return a
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def self.dot(a, b)
|
|
322
|
+
return 0 unless(a > 0 and b > 0)
|
|
323
|
+
|
|
324
|
+
result=0
|
|
325
|
+
tv=a
|
|
326
|
+
(0 .. 7).each do
|
|
327
|
+
|i|
|
|
328
|
+
if(b & (1<<i) > 0)
|
|
329
|
+
result^=tv
|
|
330
|
+
end
|
|
331
|
+
tv=xtime(tv)
|
|
332
|
+
end
|
|
333
|
+
return result
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
# _Not_ the same as dot()
|
|
338
|
+
# Multiplies a by b. In polynomial space. Without capping the value.
|
|
339
|
+
def self.mul(a, b)
|
|
340
|
+
result=0
|
|
341
|
+
tv=a
|
|
342
|
+
(0 .. 7).each do
|
|
343
|
+
|i|
|
|
344
|
+
if(b & (1<<i) > 0)
|
|
345
|
+
result^=tv
|
|
346
|
+
end
|
|
347
|
+
tv<<=1
|
|
348
|
+
end
|
|
349
|
+
return result
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
# The inverse of mul() above.
|
|
353
|
+
|
|
354
|
+
def self.div(a, b)
|
|
355
|
+
acc=a
|
|
356
|
+
tv=b
|
|
357
|
+
result=0
|
|
358
|
+
(0 .. 7).to_a.reverse.each do
|
|
359
|
+
| i |
|
|
360
|
+
tv=b<<i
|
|
361
|
+
|
|
362
|
+
if( (tv&~acc) < acc or (acc^tv) <= (1<<i))
|
|
363
|
+
result|=(1<<i)
|
|
364
|
+
acc^=tv
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
return result
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
# 8-bit number in, 8-bit number out
|
|
371
|
+
def self.mult_inverse(num)
|
|
372
|
+
return 0 unless num > 0
|
|
373
|
+
remainder=[POLYNOMIAL_SPACE, num]
|
|
374
|
+
auxiliary=[0,1]
|
|
375
|
+
|
|
376
|
+
if(remainder[1]==1)
|
|
377
|
+
return 1
|
|
378
|
+
end
|
|
379
|
+
i=2
|
|
380
|
+
while remainder[i-1]!=1
|
|
381
|
+
quotient=div(remainder[i-2], remainder[i-1])
|
|
382
|
+
multiplied=mul(remainder[i-1], quotient)
|
|
383
|
+
|
|
384
|
+
remainder[i]=remainder[i-2]^multiplied
|
|
385
|
+
auxiliary[i]=mul(quotient,auxiliary[i-1]) ^ auxiliary[i-2]
|
|
386
|
+
if (i>10)
|
|
387
|
+
raise "BUG: Multiplicative inverse should never exceed 10 iterations"
|
|
388
|
+
end
|
|
389
|
+
i+=1
|
|
390
|
+
end
|
|
391
|
+
return auxiliary[i-1]
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
def self.sbox(b)
|
|
395
|
+
c=0x63
|
|
396
|
+
b=mult_inverse(b)
|
|
397
|
+
result=b
|
|
398
|
+
(1 .. 4).each do
|
|
399
|
+
|i|
|
|
400
|
+
b_t=((b<<i)&0xff)|(b>>(8-i))
|
|
401
|
+
result^=b_t
|
|
402
|
+
end
|
|
403
|
+
return result^c
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
# Startup caching follows
|
|
407
|
+
|
|
408
|
+
unless(defined? @@all_cached)
|
|
409
|
+
@@sbox=(0 .. 255).to_a.map { |input| sbox(input)}
|
|
410
|
+
@@inv_sbox=Array.new(256)
|
|
411
|
+
(0 .. 255).each do
|
|
412
|
+
|input|
|
|
413
|
+
@@inv_sbox[@@sbox[input]]=input
|
|
414
|
+
end
|
|
415
|
+
@@dot_cache=(0 .. 0xf).map {Array.new(256)}
|
|
416
|
+
[0x2, 0x3, 0x9, 0xb, 0xd, 0xe].each do
|
|
417
|
+
# These are the only numbers we need.
|
|
418
|
+
|a|
|
|
419
|
+
(0 .. 0xff).each do
|
|
420
|
+
|b|
|
|
421
|
+
@@dot_cache[a][b]=dot(a, b)
|
|
422
|
+
end
|
|
423
|
+
end
|
|
424
|
+
@@all_cached=1
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
end
|
|
430
|
+
end
|