-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
400 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
require 'flexirest' | ||
require_relative './crypto' | ||
|
||
module Mifiel | ||
require 'mifiel/errors' | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} | ||
] | ||
} |
Oops, something went wrong.