Skip to content

Commit

Permalink
Merge 322416a into 5ab16e3
Browse files Browse the repository at this point in the history
  • Loading branch information
aldosolorzano authored Nov 20, 2018
2 parents 5ab16e3 + 322416a commit a757c08
Show file tree
Hide file tree
Showing 12 changed files with 400 additions and 15 deletions.
10 changes: 0 additions & 10 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,3 @@ source 'https://rubygems.org'

# Specify your gem's dependencies in mifiel.gemspec
gemspec

group :development, :test do
gem 'coveralls', require: false
gem 'guard', '2.13.0'
gem 'guard-rspec'
# compatible with ruby >= 1.9.3
gem 'guard-rubocop'
gem 'listen', '3.0.8'
gem 'terminal-notifier-guard'
end
5 changes: 5 additions & 0 deletions lib/crypto.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module Crypto
autoload :PBE, 'crypto/pbe'
autoload :Response, 'crypto/response'
autoload :AES, 'crypto/aes'
end
67 changes: 67 additions & 0 deletions lib/crypto/aes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
require 'openssl'

module Crypto
class AES
CIPHER = 256
SIZE = 16

def self.random_iv(size = SIZE)
OpenSSL::Random.random_bytes(size)
end

def self.encrypt(args)
aes = Crypto::AES.new(args[:cipher] || CIPHER)
aes.encrypt(args)
end

def self.decrypt(args)
aes = Crypto::AES.new(args[:cipher] || CIPHER)
aes.decrypt(args)
end

attr_accessor :cipher, :require_args

def initialize(cipher_type = CIPHER)
@require_args = [:key, :iv, :data]
@cipher = OpenSSL::Cipher::AES.new(cipher_type, :CBC)
end

def random_iv(size = SIZE)
Crypto::AES.random_iv(size)
end

def encrypt(args)
validate_args(args)
cipher.encrypt
cipher.key = args[:key]
cipher.iv = args[:iv]
encrypted_data = cipher.update(args[:data]) + cipher.final
Encrypted.new(encrypted_data)
end

def decrypt(args)
validate_args(args)
cipher.decrypt
cipher.key = args[:key]
cipher.iv = args[:iv]
cipher.update(args[:data]) + cipher.final
end

private

def validate_args(args)
keys = args.keys
require_args.each do |a|
unless keys.include?(a)
raise ArgumentError, "Expected keys #{require_args}"
end
end
end
end

class Encrypted < Crypto::Response
def to_str
data
end
end
end
50 changes: 50 additions & 0 deletions lib/crypto/pbe.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
require 'openssl'
require 'securerandom'

module Crypto
class PBE
ALPHA_NUM = ('0'..'9').to_a + ('A'..'Z').to_a + ('a'..'z').to_a
SPECIALS = ['-', '_', '+', '=', '#', '&', '*', '.'].freeze
CHARS = ALPHA_NUM + SPECIALS

def self.generate
pbe = Crypto::PBE.new
password = pbe.random_password
salt = pbe.random_salt
pbe.derived_key(password, salt)
end

def initialize(i = 1000)
@iterations = i
@digest = OpenSSL::Digest::SHA256.new
end

def random_password(length = 32)
CHARS.sort_by { SecureRandom.random_number }.join[0...length]
end

def random_salt(size = 16)
SecureRandom.random_bytes(size)
end

def derived_key(password, salt, sizeKey = 24)
args = {
password: password,
salt: salt,
iterations: @iterations,
sizeKey: sizeKey,
digest: @digest
}
PKCS5.new(args)
end
end

class PKCS5 < Crypto::Response
attr_reader :key

def initialize(args)
@key = OpenSSL::PKCS5.pbkdf2_hmac(*args.values)
@data = key # key attribute is syntax sugar
end
end
end
17 changes: 17 additions & 0 deletions lib/crypto/response.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module Crypto
class Response
attr_reader :data

def ==(other)
data == other.data
end

def initialize(data)
@data = data
end

def to_hex
data.unpack('H*').first
end
end
end
1 change: 1 addition & 0 deletions lib/mifiel.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'flexirest'
require_relative './crypto'

module Mifiel
require 'mifiel/errors'
Expand Down
3 changes: 1 addition & 2 deletions mifiel.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ Gem::Specification.new do |spec|

spec.add_development_dependency 'bump', '~> 0.5', '>= 0.5.3'
spec.add_development_dependency 'bundler', '~> 1.6'
spec.add_development_dependency 'byebug', '~> 9.0', '< 9.0.6'
spec.add_development_dependency 'pry-byebug', '~> 3.4', '>= 3.3.0'
spec.add_development_dependency 'coveralls', '~> 0.8', '>= 0.8.22'
spec.add_development_dependency 'rake', '~> 10.0'
spec.add_development_dependency 'rspec', '~> 3.1', '>= 3.1.7'
spec.add_development_dependency 'rubocop', '0.47.1'
Expand Down
57 changes: 57 additions & 0 deletions spec/crypto/aes_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
describe Crypto::AES do
aes_fixture = JSON.parse(File.read('spec/fixtures/aes.json'), symbolize_names: true)

describe '#AES good' do
aes_fixture[:valid].each do |v|
describe v.slice(:algorithm, :key, :dataToEncrypt).to_s do
e_args = { data: v[:dataToEncrypt], key: v[:key], iv: v[:iv], cipher: v[:algorithm] }
d_args = e_args.clone
let(:aes) { Crypto::AES.new(e_args[:cipher]) }
let(:encrypted) { aes.encrypt(e_args) }
it 'should return Encrypted instance' do
expect(encrypted).to be_a Crypto::Encrypted
end
it 'should return encrypted hex' do
expect(encrypted.to_hex).to eq(v[:encrypted])
end
it 'should receive binary data and return decipher text' do
d_args[:data] = encrypted.data
expect(aes.decrypt(d_args)).to eq(v[:dataToEncrypt])
end
it 'should receive Encrypted instance & return decipher text' do
d_args[:data] = encrypted
expect(aes.decrypt(d_args)).to eq(v[:dataToEncrypt])
end
it 'should encrypt & decrypt with static method' do
encrypted_data = Crypto::AES.encrypt(e_args)
expect(encrypted_data == encrypted).to be true
expect(encrypted_data.to_hex).to eq(v[:encrypted])
d_args[:data] = encrypted_data
expect(Crypto::AES.decrypt(d_args)).to eq(v[:dataToEncrypt])
end
end
end

describe 'Generate random iv' do
let(:ivs) { Set.new }
5.times do
iv = Crypto::AES.random_iv.unpack('H*').first
it "should be unique #{iv}" do
expect(ivs.include?(iv)).to be false
ivs.add(iv)
end
end
end
end

describe '#AES bad' do
describe 'ArgumentError, sending wrong data to decrypt' do
args = { data: 'bad-data', iv: Crypto::AES.random_iv, key: 'this-aSecure-key' }
let(:expected_error) { "Expected keys #{Crypto::AES.new.require_args}" }
it 'should raise expected error' do
args = {}
expect { Crypto::AES.decrypt(args) }.to raise_error(ArgumentError, expected_error)
end
end
end
end
66 changes: 66 additions & 0 deletions spec/crypto/pbe_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
describe Crypto::PBE do
pbe_fixture = JSON.parse(File.read('spec/fixtures/pbe.json'), symbolize_names: true)
let(:alpha_num) { ('0'..'9').to_a + ('A'..'Z').to_a + ('a'..'z').to_a }
let(:specials) { ['-', '_', '+', '=', '#', '&', '*', '.'] }
let(:valid_chars) { alpha_num + specials }

describe '#PBE good' do
pbe_fixture[:valid].each do |v|
describe v.slice(:key, :salt, :keylen, :iterations).to_s do
let(:pbe) { Crypto::PBE.new(v[:iterations]) }
let(:key) { pbe.derived_key(v[:key], v[:salt], v[:keylen]) }
it 'should return a strong key' do
expect(key.to_hex).to eq(v[:result])
end
end
end

describe 'Generate random keys' do
let(:keys) { Set.new }
5.times do
key = Crypto::PBE.generate.to_hex
it "should be unique #{key}" do
expect(keys.include?(key)).to be false
keys.add(key)
end
it 'should compare keys with def==(other)' do
key = Crypto::PBE.generate
key1 = Crypto::PBE.generate
expect(key == key1).to be false
end
end
end

describe 'Random password + size' do
let(:passwords) { Set.new }
key_lens = [32, 64, 40]
5.times do
size = key_lens.sample
pass = Crypto::PBE.new.random_password(size)
it "should be unique #{pass}" do
expect(passwords.include?(pass)).to be false
passwords.add(pass)
end
it "should be size #{size}" do
expect(pass.length).to be size
end
it 'should contain specified chars' do
pass_chars = pass.chars
expect(pass_chars).to eq(pass_chars & valid_chars)
end
end
end
end

describe '#PBE bad' do
pbe_fixture[:invalid].each do |v|
describe v[:description].to_s do
let(:pbe) { Crypto::PBE.new(v[:iterations]) }
let(:error) { "integer #{v[:keylen]} too big to convert to `int'" }
it 'should raise key length error' do
expect { pbe.derived_key(v[:key], v[:salt], v[:keylen]) }.to raise_error(RangeError, error)
end
end
end
end
end
68 changes: 68 additions & 0 deletions spec/fixtures/aes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
{
"valid": [
{
"algorithm":128,
"key": "1234567890123456",
"dataToEncrypt": "cifrado de Prueba",
"iv": "6976207465737469",
"encrypted": "e69522e578832e378221b541431e64e6e704c17d8b2bdda598ebd3c716836d03"
},
{
"algorithm":128,
"key": "derivedKEY234556",
"dataToEncrypt": "test de cifrado",
"iv": "3436353837383837",
"encrypted": "2b27f69570049490f5fee3bed8c045d9"
},
{
"algorithm":128,
"key": "a9r82*9rn5Flp3/o",
"dataToEncrypt": "test de cifrado",
"iv": "1353837383861646",
"encrypted": "1b6a81b3ce5eb6ae3a239d0ee51e2699"
},

{
"algorithm":192,
"key": "123456789012345678901234",
"dataToEncrypt": "cifrado de Prueba AES-192",
"iv": "6466736438373435",
"encrypted": "0cf4ea5765b42b9088c901064c07ba7a6eec3030609673830fcbd8ac5cc77edd"
},
{
"algorithm":192,
"key": "derivedKEY234556iksRtryr",
"dataToEncrypt": "test de cifrado AES-192",
"iv": "7266736438373435",
"encrypted": "fd0fec1268dbb111babc5b8a15397ad946af61561eb6d40c36068c04ddf5d008"
},
{
"algorithm":192,
"key": "*854FrGTH/hgf_4f6h9v4dfg",
"dataToEncrypt": "encrypted decrypted data",
"iv": "2e39746438373435",
"encrypted": "43e7e6f7a56a73a60d5b619e590b91de3c79e765a37f893f122a6b3e9497f8f6"
},
{
"algorithm": 256,
"key": "12345678901234567890123456789012",
"dataToEncrypt": "cifrado de Prueba AES-256",
"iv": "39382e2e2d6438rf",
"encrypted": "60ea45d407514d4e67ef0eb12b2df8e5ea02f156cdea71eeafda0b0b54092d63"
},
{
"algorithm":256,
"key": "derivedKEY234556iksRtryrtg578hfr",
"dataToEncrypt": "test de cifrado AES-256",
"iv": "7266736438373435",
"encrypted": "6d2e185278b5a87da86941b390ac7baffee5e87ae86d877350edbe9a6077e396"
},
{
"algorithm":256,
"key": "*854FrGTH/hgf_4f6h9v4dfg*&jr-jew",
"dataToEncrypt": "test de cifrado",
"iv": "2e39746438373435",
"encrypted": "3c3dc5a978f756a576a952b4eb3ca868"
}
]
}
Loading

0 comments on commit a757c08

Please sign in to comment.