aesctr-ruby 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+ group :development do
9
+ gem "shoulda", ">= 0"
10
+ gem "rdoc", "~> 3.12"
11
+ gem "bundler", "~> 1.0.0"
12
+ gem "jeweler", "~> 1.8.3"
13
+ # gem "rcov", ">= 0"
14
+ end
@@ -0,0 +1,23 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ git (1.2.5)
5
+ jeweler (1.8.3)
6
+ bundler (~> 1.0)
7
+ git (>= 1.2.5)
8
+ rake
9
+ rdoc
10
+ json (1.6.5)
11
+ rake (0.9.2.2)
12
+ rdoc (3.12)
13
+ json (~> 1.4)
14
+ shoulda (2.11.3)
15
+
16
+ PLATFORMS
17
+ ruby
18
+
19
+ DEPENDENCIES
20
+ bundler (~> 1.0.0)
21
+ jeweler (~> 1.8.3)
22
+ rdoc (~> 3.12)
23
+ shoulda
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Diego Suarez
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,23 @@
1
+ = aesctr-ruby
2
+
3
+ Aunque puede que se deba a lo torpe que soy, tras mucho buscar no he encontrado ninguna implementacion del cifrado AES-CTR (modo contador) en Ruby. Hay dos implementaciones de referencia en Javascript y PHP escritas por Chris Veness (http://www.movable-type.co.uk/scripts/aes.html), y hay al menos otro port que yo conozca a ActionScript, pero faltaba un port en Ruby.
4
+
5
+ Personalmente, decidi hacer el port en vez de usar otra de las implementaciones de AES por la sencilla razon de que necesitaba interoperabilidad con servicios web que utilizaban la implementacion de PHP citada.
6
+
7
+ ==Uso==
8
+ Simple:
9
+
10
+ require 'aesctr-ruby'
11
+ plain= "Hola mundo del cifrado!"
12
+ pass = "contrasena"
13
+ puts "Cadena original:"+plain
14
+ puts "Contrasena:"+pass
15
+ puts "Cadena cifrada y descifrada: "+AesCtr.decrypt(AesCtr.encrypt(plain, pass, 256), pass, 256)
16
+
17
+ Dos funciones principales, AesCtr.crypt y AesCtr.decrypt. Los parametros son el texto, la contraseña de cifrado, y el numero de bits a usar (128|192|256). "encrypt" devuelve cifrado el texto (y codificado en base64 para evitar problemas) y decrypt toma el texto cifrado (en base64 tb) y devuelve el original.
18
+
19
+ == Copyright
20
+
21
+ Copyright (c) 2012 Diego suarez. See LICENSE.txt for
22
+ further details.
23
+
@@ -0,0 +1,53 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "aesctr-ruby"
18
+ gem.homepage = "http://github.com/diegosuarez/aesctr-ruby"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{AES in counter mode for ruby}
21
+ gem.description = "La descripcion del resumen ya dice lo que hay :)"
22
+ gem.email = "diego.suargarcia@gmail.com"
23
+ gem.authors = ["Diego Suarez"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rake/testtask'
29
+ Rake::TestTask.new(:test) do |test|
30
+ test.libs << 'lib' << 'test'
31
+ test.pattern = 'test/**/test_*.rb'
32
+ test.verbose = true
33
+ end
34
+
35
+ #require 'rcov'
36
+ #Rcov::RcovTask.new do |test|
37
+ # test.libs << 'test'
38
+ # test.pattern = 'test/**/test_*.rb'
39
+ # test.verbose = true
40
+ # test.rcov_opts << '--exclude "gems/*"'
41
+ #end
42
+
43
+ task :default => :test
44
+
45
+ require 'rdoc/task'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "aesctr-ruby #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,60 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{aesctr-ruby}
8
+ s.version = "1.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Diego Suarez"]
12
+ s.date = %q{2012-02-18}
13
+ s.description = %q{La descripcion del resumen ya dice lo que hay :)}
14
+ s.email = %q{diego.suargarcia@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "LICENSE.txt",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "aesctr-ruby.gemspec",
28
+ "lib/aesctr-ruby.rb",
29
+ "test/helper.rb",
30
+ "test/test_aesctr-ruby.rb"
31
+ ]
32
+ s.homepage = %q{http://github.com/diegosuarez/aesctr-ruby}
33
+ s.licenses = ["MIT"]
34
+ s.require_paths = ["lib"]
35
+ s.rubygems_version = %q{1.3.7}
36
+ s.summary = %q{AES in counter mode for ruby}
37
+
38
+ if s.respond_to? :specification_version then
39
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
40
+ s.specification_version = 3
41
+
42
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
43
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
44
+ s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
45
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
46
+ s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
47
+ else
48
+ s.add_dependency(%q<shoulda>, [">= 0"])
49
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
50
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
51
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
52
+ end
53
+ else
54
+ s.add_dependency(%q<shoulda>, [">= 0"])
55
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
56
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
57
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
58
+ end
59
+ end
60
+
@@ -0,0 +1,283 @@
1
+ require 'base64'
2
+
3
+ module Aes
4
+ # sBox is pre-computed multiplicative inverse in GF(2^8) used in subBytes and keyExpansion [§5.1.1]
5
+ def self.sBox
6
+ [0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
7
+ 0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
8
+ 0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
9
+ 0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
10
+ 0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
11
+ 0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
12
+ 0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
13
+ 0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
14
+ 0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
15
+ 0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
16
+ 0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
17
+ 0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
18
+ 0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
19
+ 0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
20
+ 0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
21
+ 0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16]
22
+ end
23
+
24
+ # rCon is Round Constant used for the Key Expansion [1st col is 2^(r-1) in GF(2^8)] [§5.2]
25
+ def self.rCon
26
+ [ [0x00, 0x00, 0x00, 0x00],
27
+ [0x01, 0x00, 0x00, 0x00],
28
+ [0x02, 0x00, 0x00, 0x00],
29
+ [0x04, 0x00, 0x00, 0x00],
30
+ [0x08, 0x00, 0x00, 0x00],
31
+ [0x10, 0x00, 0x00, 0x00],
32
+ [0x20, 0x00, 0x00, 0x00],
33
+ [0x40, 0x00, 0x00, 0x00],
34
+ [0x80, 0x00, 0x00, 0x00],
35
+ [0x1b, 0x00, 0x00, 0x00],
36
+ [0x36, 0x00, 0x00, 0x00] ]
37
+ end
38
+
39
+
40
+ # AES Cipher function: encrypt 'input' state with Rijndael algorithm
41
+ # applies Nr rounds (10/12/14) using key schedule w for 'add round key' stage
42
+ # @param int[] input 16-byte (128-bit) input state array
43
+ # @param int[][] w Key schedule as 2D byte-array (Nr+1 x Nb bytes)
44
+ # @returns int[] Encrypted output state array
45
+
46
+ def self.cipher(input, w) #main Cipher function [§5.1]
47
+ nb = 4; #block size (in words): no of columns in state (fixed at 4 for AES)
48
+ nr = w.length/nb -1 ## no of rounds: 10/12/14 for 128/192/256-bit keys
49
+ state = [[],[],[],[]] # initialise 4xNb byte-array 'state' with input [§3.4]
50
+
51
+ (0...(4*nb)).each do |i|
52
+ state[i%4][(i/4.0).floor] = input[i]
53
+ end
54
+ state = self.addRoundKey(state,w,0,nb)
55
+ (1...nr).each do |round|
56
+ state = Aes.subBytes(state, nb)
57
+ state = Aes.shiftRows(state, nb)
58
+ state = Aes.mixColumns(state, nb)
59
+ state = Aes.addRoundKey(state, w,round,nb)
60
+ end
61
+ state = Aes.subBytes(state, nb)
62
+ state = Aes.shiftRows(state, nb)
63
+ state = Aes.addRoundKey(state, w, nr,nb)
64
+
65
+ output = []
66
+ (0...(4*nb)).each{|i| output[i] = state[i%4][(i/4.0).floor]}
67
+ output
68
+ end
69
+
70
+ # Perform Key Expansion to generate a Key Schedule
71
+ #
72
+ # @param int[] key Key as 16/24/32-byte array
73
+ # @returns int[][] Expanded key schedule as 2D byte-array (Nr+1 x Nb bytes)
74
+ def self.keyExpansion(key)
75
+ nb = 4 # block size (in words): no of columns in state (fixed at 4 for AES)
76
+ nk = key.length/4 # key length (in words): 4/6/8 for 128/192/256-bit keys
77
+ nr = nk + 6 # no of rounds: 10/12/14 for 128/192/256-bit keys
78
+
79
+ w = []
80
+ temp = []
81
+ 0.upto(nk-1){|i| w[i] = [key[4*i], key[4*i+1], key[4*i+2], key[4*i+3]] }
82
+ (nk...(nb*(nr+1))).each do |i|
83
+ w[i] = []
84
+ 0.upto(3){|t| temp[t] = w[i-1][t]}
85
+ if(i % nk == 0)
86
+ temp = self.subWord(self.rotWord(temp))
87
+ 0.upto(3){|t| temp[t] ^= self.rCon[i/nk][t]}
88
+ elsif(nk > 6 && i%nk==4)
89
+ temp = self.subWord(temp)
90
+ end
91
+ 0.upto(3){|t| w[i][t] = w[i-nk][t] ^ temp[t] }
92
+ end
93
+ w
94
+ end
95
+
96
+ private
97
+ # apply SBox to state S [§5.1.1]
98
+ def self.subBytes(s,nb)
99
+ 0.upto(3) do |r|
100
+ 0.upto(nb-1){|c| s[r][c] = self.sBox[s[r][c]]}
101
+ end
102
+ s
103
+ end
104
+ # shift row r of state S left by r bytes [§5.1.2]
105
+ def self.shiftRows(s,nb)
106
+ t = []
107
+ 1.upto(3) do |r|
108
+ 0.upto(3){|c| t[c] = s[r][(c+r)%nb]} # shift into temp copy
109
+ 0.upto(3){|c| s[r][c] = t[c]} # and copy back
110
+ end # note that this will work for nb =4,5,6, but not 7,8 (always 4 for AES):
111
+ s # see asmaes.sourceforge.net/rijndael/rijndaelImplementation.pdf
112
+ end
113
+
114
+ # combine bytes of each col of state S [§5.1.3]
115
+ def self.mixColumns(s, nb)
116
+ 0.upto(3) do |c|
117
+ a=[] # 'a' is a copy of the current column from 's'
118
+ b=[] # 'b' is a•{02} in GF(2^8)
119
+ 0.upto(3) do |i|
120
+ a[i] = s[i][c]
121
+ b[i] = (s[i][c]&0x80==0) ? (s[i][c]<<1) ^ 0x011b : s[i][c]<<1
122
+ end
123
+ # a[n] ^ b[n] is a•{03} in GF(2^8)
124
+ s[0][c] = b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3] # 2*a0 + 3*a1 + a2 + a3
125
+ s[1][c] = a[0] ^ b[1] ^ a[2] ^ b[2] ^ a[3] # a0 * 2*a1 + 3*a2 + a3
126
+ s[2][c] = a[0] ^ a[1] ^ b[2] ^ a[3] ^ b[3] # a0 + a1 + 2*a2 + 3*a3
127
+ s[3][c] = a[0] ^ b[0] ^ a[1] ^ a[2] ^ b[3] # 3*a0 + a1 + a2 + 2*a3
128
+ end
129
+ s
130
+ end
131
+
132
+ # xor Round Key into state S [§5.1.4]
133
+ def self.addRoundKey(state, w, rnd, nb)
134
+ 0.upto(3) do |r|
135
+ 0.upto(nb-1){|c| state[r][c] ^= w[rnd*4+c][r]}
136
+ end
137
+ state
138
+ end
139
+
140
+ # apply SBox to 4-byte word w
141
+ def self.subWord(w)
142
+ 0.upto(3){|i| w[i] = self.sBox[w[i]]}
143
+ w
144
+ end
145
+
146
+ # rotate 4-byte word w left by one byte
147
+ def self.rotWord(w)
148
+ tmp = w[0]
149
+ 0.upto(2){|i| w[i] = w[i+1]}
150
+ w[3] = tmp;
151
+ w
152
+ end
153
+
154
+ end #AES
155
+
156
+ module AesCtr
157
+ #
158
+ # Encrypt a text using AES encryption in Counter mode of operation
159
+ #
160
+ # Unicode multi-byte character safe
161
+ #
162
+ # @param string plaintext Source text to be encrypted
163
+ # @param string password The password to use to generate a key
164
+ # @param int nBits Number of bits to be used in the key (128, 192, or 256)
165
+ # @returns string Encrypted text
166
+ def self.encrypt(plaintext, password, nBits)
167
+ blockSize = 16 # block size fixed at 16 bytes / 128 bits (Nb=4) for AES
168
+ return '' unless(nBits==128 || nBits==192 || nBits==256)
169
+
170
+ # use AES itself to encrypt password to get cipher key (using plain password as source for key
171
+ # expansion) - gives us well encrypted key (though hashed key might be preferred for prod'n use)
172
+ nBytes = nBits/8 # no bytes in key (16/24/32)
173
+ pwBytes = []
174
+ 0.upto(nBytes-1){|i| pwBytes[i] = password.bytes.to_a[i] & 0xff || 0} # use 1st 16/24/32 chars of password for key #warn
175
+ key = Aes::cipher(pwBytes, Aes::keyExpansion(pwBytes)) # gives us 16-byte key
176
+ key = key + key[0, nBytes-16] # expand key to 16/24/32 bytes long
177
+ # initialise 1st 8 bytes of counter block with nonce (NIST SP800-38A §B.2): [0-1] = millisec,
178
+ # [2-3] = random, [4-7] = seconds, together giving full sub-millisec uniqueness up to Feb 2106
179
+ counterBlock = []
180
+ nonce = Time.now.to_i #42200
181
+ nonceMs = nonce%1000
182
+ nonceSec = (nonce/1000.0).floor
183
+ nonceRnd = (rand() *0xffff).floor #13
184
+ 0.upto(1){|i| counterBlock[i] = self.urs(nonceMs, i*8) &0xff }
185
+ 0.upto(1){|i| counterBlock[i+2] = self.urs(nonceRnd, i*8) &0xff }
186
+ 0.upto(3){|i| counterBlock[i+4] = self.urs(nonceSec,i*8) & 0xff}
187
+
188
+ # and convert it to a string to go on the front of the ciphertext
189
+ ctrTxt = ''
190
+ 0.upto(7){|i| ctrTxt += counterBlock[i].chr}
191
+ # generate key schedule - an expansion of the key into distinct Key Rounds for each round
192
+ keySchedule = Aes.keyExpansion(key)
193
+ #p "rks:",keySchedule
194
+ blockCount = (plaintext.length/blockSize.to_f).ceil
195
+
196
+ ciphertxt = []
197
+ 0.upto(blockCount-1) do |b|
198
+ # set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes)
199
+ # done in two stages for 32-bit ops: using two words allows us to go past 2^32 blocks (68GB)
200
+ 0.upto(3){|c| counterBlock[15-c] = self.urs(b,c*8) & 0xff}
201
+ 0.upto(3){|c| counterBlock[15-c-4] = self.urs(b/0x100000000,c*8)}
202
+
203
+ cipherCntr = Aes.cipher(counterBlock, keySchedule) # -- encrypt counter block --
204
+ # block size is reduced on final block
205
+ blockLength = b < blockCount-1 ? blockSize : (plaintext.length-1) % blockSize + 1
206
+ cipherChar = [];
207
+ 0.upto(blockLength-1) do |i|
208
+ cipherChar[i] = (cipherCntr[i] ^ plaintext.bytes.to_a[b*blockSize+i]).chr
209
+ end
210
+ ciphertxt[b] = cipherChar.join('')
211
+ end
212
+
213
+ ciphertext = ctrTxt + ciphertxt.join('')
214
+ ciphertext = Base64.encode64(ciphertext).gsub(/\n/,'')+"\n"; # encode in base64
215
+ end
216
+
217
+ # Decrypt a text encrypted by AES in counter mode of operation
218
+ #
219
+ # @param string ciphertext Source text to be encrypted
220
+ # @param string password The password to use to generate a key
221
+ # @param int nBits Number of bits to be used in the key (128, 192, or 256)
222
+ # @returns string
223
+ # Decrypted text
224
+ def self.decrypt(ciphertext, password, nBits)
225
+ blockSize = 16 # block size fixed at 16 bytes / 128 bits (Nb=4) for AES
226
+ return '' unless(nBits==128 || nBits==192 || nBits==256)
227
+ ciphertext = Base64.decode64(ciphertext);
228
+
229
+ nBytes = nBits/8 # no bytes in key (16/24/32)
230
+ pwBytes = []
231
+ 0.upto(nBytes-1){|i| pwBytes[i] = password.bytes.to_a[i] & 0xff || 0}
232
+ key = Aes::cipher(pwBytes, Aes::keyExpansion(pwBytes)) # gives us 16-byte key
233
+ key = key.concat(key.slice(0, nBytes-16)) # expand key to 16/24/32 bytes long
234
+ # recover nonce from 1st 8 bytes of ciphertext
235
+ counterBlock = []
236
+ ctrTxt = ciphertext[0,8]
237
+ 0.upto(7){|i| counterBlock[i] = ctrTxt.bytes.to_a[i]}
238
+
239
+ #generate key Schedule
240
+ keySchedule = Aes.keyExpansion(key);
241
+
242
+ # separate ciphertext into blocks (skipping past initial 8 bytes)
243
+ nBlocks = ((ciphertext.length-8)/blockSize.to_f).ceil
244
+ ct=[]
245
+ 0.upto(nBlocks-1){|b|ct[b] = ciphertext[8+b*blockSize, 16]}
246
+
247
+ ciphertext = ct; # ciphertext is now array of block-length strings
248
+
249
+ # plaintext will get generated block-by-block into array of block-length strings
250
+ plaintxt = [];
251
+ 0.upto(nBlocks-1) do |b|
252
+ 0.upto(3){|c| counterBlock[15-c] = self.urs(b,c*8) & 0xff}
253
+ 0.upto(3){|c| counterBlock[15-c-4] = self.urs((b+1)/(0x100000000-1),c*8) & 0xff}
254
+ cipherCntr = Aes.cipher(counterBlock, keySchedule) # encrypt counter block
255
+ plaintxtByte = []
256
+ 0.upto(ciphertext[b].length - 1) do |i|
257
+ # -- xor plaintxt with ciphered counter byte-by-byte --
258
+ plaintxtByte[i] = (cipherCntr[i] ^ ciphertext[b].bytes.to_a[i]).chr;
259
+ end
260
+ plaintxt[b] = plaintxtByte.join('')
261
+ end
262
+ plaintext = plaintxt.join('')
263
+ end
264
+
265
+ private
266
+ #
267
+ # Unsigned right shift function, since Ruby has neither >>> operator nor unsigned ints
268
+ #
269
+ # @param a number to be shifted (32-bit integer)
270
+ # @param b number of bits to shift a to the right (0..31)
271
+ # @return a right-shifted and zero-filled by b bits
272
+ def self.urs(a, b)
273
+ a &= 0xffffffff
274
+ b &= 0x1f
275
+ if (a&0x80000000 && b>0) # if left-most bit set
276
+ a = ((a>>1) & 0x7fffffff) # right-shift one bit & clear left-most bit
277
+ a = a >> (b-1) # remaining right-shifts
278
+ else # otherwise
279
+ a = (a>>b); # use normal right-shift
280
+ end
281
+ a
282
+ end
283
+ end
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+ require 'shoulda'
12
+
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
15
+ require 'aesctr-ruby'
16
+
17
+ class Test::Unit::TestCase
18
+ end
@@ -0,0 +1,11 @@
1
+ require 'helper'
2
+
3
+ class TestAesctrRuby < Test::Unit::TestCase
4
+ should "be equal before and after crypt" do
5
+ plain= "Hola mundo del cifrado!"
6
+ pass = "contrasena"
7
+ puts "Cadena original:"+plain
8
+ puts "Contrasena:"+pass
9
+ assert_equal plain, AesCtr.decrypt(AesCtr.encrypt(plain, pass, 192), pass, 192)
10
+ end
11
+ end
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: aesctr-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ segments:
6
+ - 1
7
+ - 0
8
+ - 0
9
+ prerelease: false
10
+ platform: ruby
11
+ authors:
12
+ - Diego Suarez
13
+ autorequire: !!null
14
+ bindir: bin
15
+ cert_chain: []
16
+ date: 2012-02-18 00:00:00.000000000 +01:00
17
+ default_executable: !!null
18
+ dependencies:
19
+ - !ruby/object:Gem::Dependency
20
+ name: shoulda
21
+ requirement: &15647480 !ruby/object:Gem::Requirement
22
+ none: false
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ segments:
28
+ - 0
29
+ type: :development
30
+ prerelease: false
31
+ version_requirements: *15647480
32
+ - !ruby/object:Gem::Dependency
33
+ name: rdoc
34
+ requirement: &15646620 !ruby/object:Gem::Requirement
35
+ none: false
36
+ requirements:
37
+ - - ~>
38
+ - !ruby/object:Gem::Version
39
+ version: '3.12'
40
+ segments:
41
+ - 3
42
+ - 12
43
+ type: :development
44
+ prerelease: false
45
+ version_requirements: *15646620
46
+ - !ruby/object:Gem::Dependency
47
+ name: bundler
48
+ requirement: &15643680 !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 1.0.0
54
+ segments:
55
+ - 1
56
+ - 0
57
+ - 0
58
+ type: :development
59
+ prerelease: false
60
+ version_requirements: *15643680
61
+ - !ruby/object:Gem::Dependency
62
+ name: jeweler
63
+ requirement: &15642860 !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 1.8.3
69
+ segments:
70
+ - 1
71
+ - 8
72
+ - 3
73
+ type: :development
74
+ prerelease: false
75
+ version_requirements: *15642860
76
+ description: La descripcion del resumen ya dice lo que hay :)
77
+ email: diego.suargarcia@gmail.com
78
+ executables: []
79
+ extensions: []
80
+ extra_rdoc_files:
81
+ - LICENSE.txt
82
+ - README.rdoc
83
+ files:
84
+ - .document
85
+ - Gemfile
86
+ - Gemfile.lock
87
+ - LICENSE.txt
88
+ - README.rdoc
89
+ - Rakefile
90
+ - VERSION
91
+ - aesctr-ruby.gemspec
92
+ - lib/aesctr-ruby.rb
93
+ - test/helper.rb
94
+ - test/test_aesctr-ruby.rb
95
+ has_rdoc: true
96
+ homepage: http://github.com/diegosuarez/aesctr-ruby
97
+ licenses:
98
+ - MIT
99
+ post_install_message: !!null
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ! '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ segments:
110
+ - 0
111
+ hash: -2105260795538654983
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ segments:
119
+ - 0
120
+ requirements: []
121
+ rubyforge_project: !!null
122
+ rubygems_version: 1.3.7
123
+ signing_key: !!null
124
+ specification_version: 3
125
+ summary: AES in counter mode for ruby
126
+ test_files: []