forked from debreczeni/ruby-eth
-
Notifications
You must be signed in to change notification settings - Fork 1
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
333 additions
and
3 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,3 +7,4 @@ | |
/pkg/ | ||
/spec/reports/ | ||
/tmp/ | ||
.ruby-version |
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
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,87 @@ | ||
require 'json' | ||
|
||
class Eth::Key::Decrypter | ||
include Eth::Utils | ||
|
||
def self.perform(data, password) | ||
new(data, password).perform | ||
end | ||
|
||
def initialize(data, password) | ||
@data = JSON.parse(data) | ||
@password = password | ||
end | ||
|
||
def perform | ||
derive_key password | ||
check_macs | ||
bin_to_hex decrypted_data | ||
end | ||
|
||
|
||
private | ||
|
||
attr_reader :data, :key, :password | ||
|
||
def derive_key(password) | ||
@key = OpenSSL::PKCS5.pbkdf2_hmac(password, salt, iterations, key_length, digest) | ||
end | ||
|
||
def check_macs | ||
mac1 = keccak256(key[(key_length/2), key_length] + ciphertext) | ||
mac2 = hex_to_bin crypto_data['mac'] | ||
|
||
if mac1 != mac2 | ||
raise "Message Authentications Codes do not match!" | ||
end | ||
end | ||
|
||
def decrypted_data | ||
@decrypted_data ||= cipher.update(ciphertext) + cipher.final | ||
end | ||
|
||
def crypto_data | ||
@crypto_data ||= data['crypto'] || data['Crypto'] | ||
end | ||
|
||
def ciphertext | ||
hex_to_bin crypto_data['ciphertext'] | ||
end | ||
|
||
def cipher_name | ||
"aes-128-ctr" | ||
end | ||
|
||
def cipher | ||
@cipher ||= OpenSSL::Cipher.new(cipher_name).tap do |cipher| | ||
cipher.decrypt | ||
cipher.key = key[0, (key_length/2)] | ||
cipher.iv = iv | ||
end | ||
end | ||
|
||
def iv | ||
hex_to_bin crypto_data['cipherparams']['iv'] | ||
end | ||
|
||
def salt | ||
hex_to_bin crypto_data['kdfparams']['salt'] | ||
end | ||
|
||
def iterations | ||
crypto_data['kdfparams']['c'].to_i | ||
end | ||
|
||
def key_length | ||
32 | ||
end | ||
|
||
def digest | ||
OpenSSL::Digest.new digest_name | ||
end | ||
|
||
def digest_name | ||
"sha256" | ||
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,127 @@ | ||
require 'json' | ||
|
||
class Eth::Key::Encrypter | ||
include Eth::Utils | ||
|
||
def self.perform(key, password, options = {}) | ||
new(key, options).perform(password) | ||
end | ||
|
||
def initialize(key, options = {}) | ||
@key = key | ||
@options = options | ||
end | ||
|
||
def perform(password) | ||
derive_key password | ||
encrypt | ||
|
||
data.to_json | ||
end | ||
|
||
def data | ||
{ | ||
crypto: { | ||
cipher: cipher_name, | ||
cipherparams: { | ||
iv: bin_to_hex(iv), | ||
}, | ||
ciphertext: bin_to_hex(encrypted_key), | ||
kdf: "pbkdf2", | ||
kdfparams: { | ||
c: iterations, | ||
dklen: 32, | ||
prf: prf, | ||
salt: bin_to_hex(salt), | ||
}, | ||
mac: bin_to_hex(mac), | ||
}, | ||
id: id, | ||
version: 3, | ||
}.tap do |data| | ||
data[:address] = address unless options[:skip_address] | ||
end | ||
end | ||
|
||
def id | ||
@id ||= options[:id] || SecureRandom.uuid | ||
end | ||
|
||
|
||
private | ||
|
||
attr_reader :derived_key, :encrypted_key, :key, :options | ||
|
||
def cipher | ||
@cipher ||= OpenSSL::Cipher.new(cipher_name).tap do |cipher| | ||
cipher.encrypt | ||
cipher.iv = iv | ||
cipher.key = derived_key[0, (key_length/2)] | ||
end | ||
end | ||
|
||
def digest | ||
@digest ||= OpenSSL::Digest.new digest_name | ||
end | ||
|
||
def derive_key(password) | ||
@derived_key = OpenSSL::PKCS5.pbkdf2_hmac(password, salt, iterations, key_length, digest) | ||
end | ||
|
||
def encrypt | ||
@encrypted_key = cipher.update(hex_to_bin key) + cipher.final | ||
end | ||
|
||
def mac | ||
keccak256(derived_key[(key_length/2), key_length] + encrypted_key) | ||
end | ||
|
||
def cipher_name | ||
"aes-128-ctr" | ||
end | ||
|
||
def digest_name | ||
"sha256" | ||
end | ||
|
||
def prf | ||
"hmac-#{digest_name}" | ||
end | ||
|
||
def key_length | ||
32 | ||
end | ||
|
||
def salt_length | ||
32 | ||
end | ||
|
||
def iv_length | ||
16 | ||
end | ||
|
||
def iterations | ||
options[:iterations] || 262_144 | ||
end | ||
|
||
def salt | ||
@salt ||= if options[:salt] | ||
hex_to_bin options[:salt] | ||
else | ||
SecureRandom.random_bytes(salt_length) | ||
end | ||
end | ||
|
||
def iv | ||
@iv ||= if options[:iv] | ||
hex_to_bin options[:iv] | ||
else | ||
SecureRandom.random_bytes(iv_length) | ||
end | ||
end | ||
|
||
def address | ||
Eth::Key.new(priv: key).address | ||
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,3 +1,3 @@ | ||
module Eth | ||
VERSION = "0.4.2" | ||
VERSION = "0.4.3" | ||
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,13 @@ | ||
describe Eth::Key::Decrypter do | ||
|
||
describe ".perform" do | ||
let(:password) { 'testpassword' } | ||
let(:key_data) { read_key_fixture password } | ||
|
||
it "recovers the examle key" do | ||
result = Eth::Key::Decrypter.perform key_data, password | ||
expect(result).to eq('7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d') | ||
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,51 @@ | ||
describe Eth::Key::Encrypter do | ||
|
||
describe ".perform" do | ||
let(:password) { 'testpassword' } | ||
let(:key) { '7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d' } | ||
let(:uuid) { "3198bc9c-6672-5ab3-d995-4942343ae5b6" } | ||
let(:iv) { '6087dab2f9fdbbfaddc31a909735c1e6' } | ||
let(:salt) { 'ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd' } | ||
let(:options) do | ||
{ | ||
iv: iv, | ||
salt: salt, | ||
id: uuid, | ||
} | ||
end | ||
|
||
it "recovers the key" do | ||
result = Eth::Key::Encrypter.perform key, password, options | ||
json = JSON.parse(result) | ||
|
||
expect(json['address']).to eq('0x008AeEda4D805471dF9b2A5B0f38A0C3bCBA786b') | ||
expect(json['crypto']['cipher']).to eq('aes-128-ctr') | ||
expect(json['crypto']['cipherparams']['iv']).to eq(iv) | ||
expect(json['crypto']['ciphertext']).to eq('5318b4d5bcd28de64ee5559e671353e16f075ecae9f99c7a79a38af5f869aa46') | ||
expect(json['crypto']['kdf']).to eq('pbkdf2') | ||
expect(json['crypto']['kdfparams']['c']).to eq(262_144) | ||
expect(json['crypto']['kdfparams']['dklen']).to eq(32) | ||
expect(json['crypto']['kdfparams']['prf']).to eq("hmac-sha256") | ||
expect(json['crypto']['kdfparams']['salt']).to eq(salt) | ||
expect(json['crypto']['mac']).to eq('517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e9b2') | ||
expect(json['id']).to eq(uuid) | ||
expect(json['version']).to eq(3) | ||
end | ||
|
||
context "when specifying not to include the address" do | ||
let(:options) do | ||
{ | ||
skip_address: true, | ||
} | ||
end | ||
|
||
it "recovers the key" do | ||
result = Eth::Key::Encrypter.perform key, password, options | ||
json = JSON.parse(result) | ||
expect(json['address']).to be_nil | ||
expect(json.keys).not_to include 'address' | ||
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
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 @@ | ||
{ "crypto" : { "cipher" : "aes-128-ctr", "cipherparams" : { "iv" : "6087dab2f9fdbbfaddc31a909735c1e6" }, "ciphertext" : "5318b4d5bcd28de64ee5559e671353e16f075ecae9f99c7a79a38af5f869aa46", "kdf" : "pbkdf2", "kdfparams" : { "c" : 262144, "dklen" : 32, "prf" : "hmac-sha256", "salt" : "ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd" }, "mac" : "517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e9b2" }, "id" : "3198bc9c-6672-5ab3-d995-4942343ae5b6", "version" : 3 } |
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