strongroom 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/.travis.yml +1 -0
- data/Gemfile +2 -0
- data/README.md +61 -0
- data/Rakefile +20 -0
- data/lib/strongroom/decryptor.rb +29 -0
- data/lib/strongroom/encryptor.rb +33 -0
- data/lib/strongroom/enigma.rb +48 -0
- data/lib/strongroom/has_rsa_key.rb +21 -0
- data/lib/strongroom/version.rb +3 -0
- data/lib/strongroom.rb +10 -0
- data/spec/fixtures/private_key +27 -0
- data/spec/fixtures/public_key +9 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/strongroom_decryptor_spec.rb +37 -0
- data/spec/strongroom_encryptor_spec.rb +35 -0
- data/spec/strongroom_enigma_spec.rb +99 -0
- data/spec/strongroom_has_rsa_key_spec.rb +22 -0
- data/spec/strongroom_spec.rb +34 -0
- data/strongroom.gemspec +23 -0
- metadata +131 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm: 1.9.2
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
Strongroom
|
2
|
+
==========
|
3
|
+
|
4
|
+
Strong public-key encryption for arbitrary length data.
|
5
|
+
|
6
|
+
|
7
|
+
Overview
|
8
|
+
--------
|
9
|
+
|
10
|
+
Strongroom combines RSA public-key encryption and AES symmetric-key encryption to encrypt arbitrary length data with a public key, such that it can only be decrypted with the corresponding private key.
|
11
|
+
|
12
|
+
Ruby's OpenSSL bindings do all the heavy lifting; Strongroom acts as simple glue-code and as a central point for testing, documentation, collaboration and peer review.
|
13
|
+
|
14
|
+
|
15
|
+
Usage
|
16
|
+
-----
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
# Encrypt with public key:
|
20
|
+
enigma = Strongroom::Encryptor.new("spec/fixtures/public_key").encrypt("secret message")
|
21
|
+
# => #<Strongroom::Enigma AES-128-CFB ciphertext: 14 bytes, encrypted_key: 256 bytes, iv: 16 bytes>
|
22
|
+
|
23
|
+
# Store enigma as US-ASCII YAML text:
|
24
|
+
s = enigma.serialize
|
25
|
+
=> "--- \ncipher: AES-128-CFB\nciphertext: |\n 55djlRLf[...]p8w==\n\n"
|
26
|
+
|
27
|
+
# Retrieve enigma:
|
28
|
+
enigma = Strongroom::Enigma.deserialize(s)
|
29
|
+
# => #<Strongroom::Enigma AES-128-CFB ciphertext: 14 bytes, encrypted_key: 256 bytes, iv: 16 bytes>
|
30
|
+
|
31
|
+
# Decrypt with private key:
|
32
|
+
Strongroom::Decryptor.new("spec/fixtures/private_key").decrypt(enigma)
|
33
|
+
# => "secret message"
|
34
|
+
```
|
35
|
+
|
36
|
+
|
37
|
+
Status
|
38
|
+
------
|
39
|
+
|
40
|
+
[![http://travis-ci.org/pda/strongroom.png](http://travis-ci.org/pda/strongroom.png)](http://travis-ci.org/#!/pda/strongroom)
|
41
|
+
|
42
|
+
Implementation is functionally complete. Documentation is work in progress.
|
43
|
+
|
44
|
+
|
45
|
+
Reading List
|
46
|
+
------------
|
47
|
+
|
48
|
+
* [Hybrid cryptosystem](http://en.wikipedia.org/wiki/Hybrid_cryptosystem)
|
49
|
+
* [Advanced Encryption Standard](http://en.wikipedia.org/wiki/Advanced_Encryption_Standard)
|
50
|
+
* [RSA public-key encryption](http://en.wikipedia.org/wiki/RSA)
|
51
|
+
* [Block cipher modes of operation](http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation)
|
52
|
+
* [OWASP Cryptographic Storage Cheat Sheet](https://www.owasp.org/index.php/Cryptographic_Storage_Cheat_Sheet)
|
53
|
+
* [OpenSSL::Cipher (Ruby stdlib)](http://ruby-doc.org/stdlib/libdoc/openssl/rdoc/classes/OpenSSL/Cipher.html)
|
54
|
+
* [OpenSSL::PKey::RSA (Ruby stdlib)](http://ruby-doc.org/stdlib/libdoc/openssl/rdoc/classes/OpenSSL/PKey/RSA.html)
|
55
|
+
|
56
|
+
|
57
|
+
License
|
58
|
+
-------
|
59
|
+
|
60
|
+
© 2011 Learnable Pty Ltd
|
61
|
+
Open Source under the [The MIT License](http://www.opensource.org/licenses/mit-license.php).
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
|
3
|
+
task default: :test
|
4
|
+
|
5
|
+
# Bundler
|
6
|
+
require "bundler/gem_tasks"
|
7
|
+
|
8
|
+
# MiniTest
|
9
|
+
require "rake/testtask"
|
10
|
+
Rake::TestTask.new do |t|
|
11
|
+
ENV["TESTOPTS"] = "-v"
|
12
|
+
t.pattern = "spec/*_spec.rb"
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Generate SDoc documentation"
|
16
|
+
task :sdoc do
|
17
|
+
# SDoc support for top-level classnames:
|
18
|
+
# https://github.com/voloko/sdoc/pull/26
|
19
|
+
sh "sdoc lib --main ::Strongroom"
|
20
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Strongroom
|
2
|
+
class Decryptor
|
3
|
+
|
4
|
+
include HasRsaKey
|
5
|
+
|
6
|
+
def initialize private_key, cipher_locator = nil
|
7
|
+
@cipher_locator = cipher_locator || CipherLocator.new
|
8
|
+
has_rsa_key private_key
|
9
|
+
end
|
10
|
+
|
11
|
+
def decrypt enigma
|
12
|
+
cipher = @cipher_locator.for(enigma)
|
13
|
+
cipher.decrypt
|
14
|
+
cipher.key = rsa_key.private_decrypt enigma.encrypted_key
|
15
|
+
cipher.iv = enigma.iv
|
16
|
+
cipher.update(enigma.ciphertext) << cipher.final
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
attr_reader :cipher
|
21
|
+
|
22
|
+
class CipherLocator
|
23
|
+
def for enigma
|
24
|
+
OpenSSL::Cipher.new enigma.cipher
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Strongroom
|
2
|
+
class Encryptor
|
3
|
+
|
4
|
+
include HasRsaKey
|
5
|
+
|
6
|
+
def initialize public_key, cipher = nil
|
7
|
+
@cipher = cipher if cipher
|
8
|
+
has_rsa_key public_key
|
9
|
+
end
|
10
|
+
|
11
|
+
def encrypt input
|
12
|
+
key = cipher.random_key
|
13
|
+
iv = cipher.random_iv
|
14
|
+
|
15
|
+
cipher.encrypt
|
16
|
+
cipher.key = key
|
17
|
+
cipher.iv = iv
|
18
|
+
|
19
|
+
Enigma.new \
|
20
|
+
cipher: cipher.name,
|
21
|
+
ciphertext: cipher.update(input) << cipher.final,
|
22
|
+
encrypted_key: rsa_key.public_encrypt(key),
|
23
|
+
iv: iv
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def cipher
|
29
|
+
@cipher ||= OpenSSL::Cipher.new("AES-128-CFB")
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require "base64"
|
2
|
+
|
3
|
+
module Strongroom
|
4
|
+
class Enigma
|
5
|
+
|
6
|
+
def initialize parameters
|
7
|
+
@cipher = parameters.fetch :cipher
|
8
|
+
@ciphertext = parameters.fetch :ciphertext
|
9
|
+
@encrypted_key = parameters.fetch :encrypted_key
|
10
|
+
@iv = parameters.fetch :iv
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_accessor :cipher, :ciphertext, :encrypted_key, :iv
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
keys = [ :ciphertext, :encrypted_key, :iv ]
|
17
|
+
"#<Strongroom::Enigma #{cipher} %s>" %
|
18
|
+
keys.map { |key| "#{key}: #{send(key).length} bytes" }.join(", ")
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_hash
|
22
|
+
{
|
23
|
+
"cipher" => cipher,
|
24
|
+
"ciphertext" => Base64.encode64(ciphertext),
|
25
|
+
"encrypted_key" => Base64.encode64(encrypted_key),
|
26
|
+
"iv" => Base64.encode64(iv)
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.from_hash hash
|
31
|
+
new(
|
32
|
+
cipher: hash["cipher"],
|
33
|
+
ciphertext: Base64.decode64(hash["ciphertext"]),
|
34
|
+
encrypted_key: Base64.decode64(hash["encrypted_key"]),
|
35
|
+
iv: Base64.decode64(hash["iv"])
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
def serialize
|
40
|
+
to_hash.to_yaml.encode!("US-ASCII")
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.deserialize input
|
44
|
+
from_hash YAML.load(input)
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Strongroom
|
2
|
+
module HasRsaKey
|
3
|
+
|
4
|
+
private
|
5
|
+
|
6
|
+
attr_reader :rsa_key
|
7
|
+
|
8
|
+
def has_rsa_key key
|
9
|
+
@rsa_key = resolve_rsa_key key
|
10
|
+
end
|
11
|
+
|
12
|
+
def resolve_rsa_key key
|
13
|
+
if key.is_a? String
|
14
|
+
OpenSSL::PKey::RSA.new(File.read(key))
|
15
|
+
else
|
16
|
+
key
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
data/lib/strongroom.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
2
|
+
MIIEowIBAAKCAQEAqvE4o/NSsYxKSxEeCU9lx1KJ3IE8bCO6p55E5XxWiS4k2ig+
|
3
|
+
dBKB/VcUPkTIHD42LtiY5nbwdMscs5sd2lew+rxeeZg6HSD4e0bHy5axT2UvrDbf
|
4
|
+
4wSCDcEDllQa6JeurtRZcsBjFRq5SV3DkQ1CPmXmZxHyCkmOXYQz8kUeIiPyd9DB
|
5
|
+
m7Pxc9hDcZqrAiTLxyLvPjof5ESbk8Iq/FYjm1W8BTrJZtHgdw81jON5OaFZSAO+
|
6
|
+
4RA2ktsPa/4SgrHmO3B6iVhQdL+KjKhOUccLRbZG0enXWLD8SWCTIsdw2FBWGxT2
|
7
|
+
44DxIX0eotQjAVkWcm2+sB4NB24YWi78xK5IcQIDAQABAoIBAQCEctEbH15UWUn7
|
8
|
+
LfEi75UBwq803iHN/EUUuOnTEesO8WT2b7YsQ2bav4zcznhqgb2DwKl/8GpRZEsB
|
9
|
+
0s9nrQhgznE5L7zfcvt0sRv/X+xUgydT/VSt1oFaXNIFuXb7/wMHTVtdMTnUUVd9
|
10
|
+
RBxSmH5aUKQb+mialfGr/dIuBmXZttE8YgH2E456RvJGKu0kvsWSd3CVyYVKDSBV
|
11
|
+
MUxXCh+tfbwTucv2skT5sOLS+qGjcx5ToJs5EtieF7El4KPzUTuEmYvnkpxB6Q3e
|
12
|
+
nGQRC98I9FbN3E8OGALRwo/B0H0YGoqhh2rH8QlcwAZ4DMcuCOAv5kjE7NmY5k0N
|
13
|
+
tSwOiYfRAoGBAOPhEaQ3HX7BazW2cY5X5zF9ch8d4KNd6VqX1FYa9NK9uc552rMP
|
14
|
+
xMSL+NPw28XqJaMhZ8bwnCVS9flxTJukj2luAutyl/RvpO8XoL78bU6/dQkmIk00
|
15
|
+
rmn8DGLyWk2Jt+ap/ymBXVz38DdozlGHrDRPoxAu251jWl2WfXGivsktAoGBAMAJ
|
16
|
+
dWRTZMq95KY23zAECM6WMdazA9eBTDvBJw85My7WeS3pUYTfFrCBWoyai6wpSljD
|
17
|
+
osFL645zCfKgL2Ahhyf17kEwDpGB0/PPcRBkdcgYb1nsHOWbEZXbgYspChLk41j9
|
18
|
+
5dTOv3lfgx3dlNVEdRvOZTlcUpyKl9DduflXGD7VAoGAa6ljP1V2zvMs5sSao+it
|
19
|
+
5vjgmQjxn913qYCAJmo37vUKOx3hEKZLjZyf4+owuzHjtE3NDPWSoCOw6Lf5vLMj
|
20
|
+
umjqYjjSx/6TSw67uF/keMcF0Vu96wIPwSz+4SSlO2rsgsMgNYjUBl9xk4wlpelW
|
21
|
+
l6RkYvnwckyFXiAChlSglMUCgYApuSRfUtsgTZfDIULmrMm5ENrtOeHIzdfhV7sD
|
22
|
+
zLq5H3IeroW+p7XHTaN9zwovspzroj3XX4ZjYc00gOcqLL6vyPgmA6n2pU2GuHhp
|
23
|
+
RlpsyhpPYER7AnEUrSd9M8JzjnVy3V81GGJznXPgZkcy+veyveoyR0PPrTEE+Vex
|
24
|
+
u+GRrQKBgHtiZAx1E4Fm7TgXYTjDgsxfYrC//pb74Eno0wObT6aIWOFlyu/v63Cv
|
25
|
+
gMLbdTU+KJuV1sypn2m3/Zb43VIOGswuaGt3y7prNFCuOiPNFodm0YDz9DLHrEIg
|
26
|
+
jM8xYfJqxzp0/tnbwjO5Gc+V+pOs0LlAV3xmmtkyAjDXY3/3jRPU
|
27
|
+
-----END RSA PRIVATE KEY-----
|
@@ -0,0 +1,9 @@
|
|
1
|
+
-----BEGIN PUBLIC KEY-----
|
2
|
+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqvE4o/NSsYxKSxEeCU9l
|
3
|
+
x1KJ3IE8bCO6p55E5XxWiS4k2ig+dBKB/VcUPkTIHD42LtiY5nbwdMscs5sd2lew
|
4
|
+
+rxeeZg6HSD4e0bHy5axT2UvrDbf4wSCDcEDllQa6JeurtRZcsBjFRq5SV3DkQ1C
|
5
|
+
PmXmZxHyCkmOXYQz8kUeIiPyd9DBm7Pxc9hDcZqrAiTLxyLvPjof5ESbk8Iq/FYj
|
6
|
+
m1W8BTrJZtHgdw81jON5OaFZSAO+4RA2ktsPa/4SgrHmO3B6iVhQdL+KjKhOUccL
|
7
|
+
RbZG0enXWLD8SWCTIsdw2FBWGxT244DxIX0eotQjAVkWcm2+sB4NB24YWi78xK5I
|
8
|
+
cQIDAQAB
|
9
|
+
-----END PUBLIC KEY-----
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require "simplecov"
|
2
|
+
SimpleCov.start if ENV["SIMPLECOV"]
|
3
|
+
|
4
|
+
require "pathname"
|
5
|
+
$LOAD_PATH.unshift Pathname(__FILE__).dirname.parent.join("lib").to_path
|
6
|
+
|
7
|
+
require "minitest/autorun"
|
8
|
+
|
9
|
+
require "strongroom"
|
10
|
+
|
11
|
+
def fixture_path file
|
12
|
+
Pathname.new(__FILE__).dirname.join("fixtures").join(file).to_path
|
13
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
describe Strongroom::Decryptor do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@cipher = ::MiniTest::Mock.new.tap do |c|
|
7
|
+
c.expect :decrypt, nil
|
8
|
+
c.expect :key=, "plainkey", [ "plainkey" ]
|
9
|
+
c.expect :iv=, "iv", [ "iv" ]
|
10
|
+
c.expect :update, "original inp", [ "ciphertext==" ]
|
11
|
+
c.expect :final, "ut"
|
12
|
+
end
|
13
|
+
@key = ::MiniTest::Mock.new.tap do |k|
|
14
|
+
k.expect :private_decrypt, "plainkey", [ "ekey" ]
|
15
|
+
end
|
16
|
+
@enigma = ::MiniTest::Mock.new.tap do |e|
|
17
|
+
e.expect :ciphertext, "ciphertext=="
|
18
|
+
e.expect :encrypted_key, "ekey"
|
19
|
+
e.expect :iv, "iv"
|
20
|
+
end
|
21
|
+
@cipher_locator = ::MiniTest::Mock.new.tap do |cl|
|
22
|
+
cl.expect :for, @cipher, [ @enigma ]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it "calls OpenSSL methods" do
|
27
|
+
Strongroom::Decryptor.new(@key, @cipher_locator).decrypt(@enigma)
|
28
|
+
[ @cipher, @key, @enigma ].each &:verify
|
29
|
+
end
|
30
|
+
|
31
|
+
it "returns correct plaintext" do
|
32
|
+
Strongroom::Decryptor.new(@key, @cipher_locator).decrypt(@enigma).tap do |output|
|
33
|
+
output.must_equal "original input"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
describe Strongroom::Encryptor do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@cipher = ::MiniTest::Mock.new.tap do |c|
|
7
|
+
c.expect :name, "AES-128-TEST"
|
8
|
+
c.expect :encrypt, nil
|
9
|
+
c.expect :random_key, "rkey"
|
10
|
+
c.expect :key=, "rkey", [ "rkey" ]
|
11
|
+
c.expect :random_iv, "riv"
|
12
|
+
c.expect :iv=, "riv", [ "riv" ]
|
13
|
+
c.expect :update, "cipherte", [ "input" ]
|
14
|
+
c.expect :final, "xt=="
|
15
|
+
end
|
16
|
+
@key = ::MiniTest::Mock.new.tap do |k|
|
17
|
+
k.expect :public_encrypt, "encryptedkey", [ "rkey" ]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it "calls OpenSSL methods" do
|
22
|
+
Strongroom::Encryptor.new(@key, @cipher).encrypt("input")
|
23
|
+
[ @cipher, @key ].each &:verify
|
24
|
+
end
|
25
|
+
|
26
|
+
it "creates correct Enigma" do
|
27
|
+
Strongroom::Encryptor.new(@key, @cipher).encrypt("input").tap do |enigma|
|
28
|
+
enigma.ciphertext.must_equal "ciphertext=="
|
29
|
+
enigma.encrypted_key.must_equal "encryptedkey"
|
30
|
+
enigma.iv.must_equal "riv"
|
31
|
+
enigma.cipher.must_equal "AES-128-TEST"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
describe Strongroom::Enigma do
|
4
|
+
|
5
|
+
# binary guaranteed to be invalid as US-ASCII or UTF-8
|
6
|
+
def binary input
|
7
|
+
"\xFF" << input
|
8
|
+
end
|
9
|
+
|
10
|
+
# input has non-valid-character-encoding byte added, then base64 encoded
|
11
|
+
def bin64 input
|
12
|
+
Base64.encode64 binary(input)
|
13
|
+
end
|
14
|
+
|
15
|
+
def enigma ciphertext = "ct", encrypted_key = "ek", iv = "iv"
|
16
|
+
Strongroom::Enigma.new \
|
17
|
+
cipher: "AES-128-TEST",
|
18
|
+
ciphertext: binary(ciphertext),
|
19
|
+
encrypted_key: binary(encrypted_key),
|
20
|
+
iv: binary(iv)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "stores ciphertext, encrypted_key, iv" do
|
24
|
+
enigma.ciphertext.must_equal binary("ct")
|
25
|
+
enigma.encrypted_key.must_equal binary("ek")
|
26
|
+
enigma.iv.must_equal binary("iv")
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#to_s" do
|
30
|
+
it "returns a string with byte counts" do
|
31
|
+
s = enigma("abc", "1234567", "xz").to_s
|
32
|
+
s.must_equal "#<Strongroom::Enigma AES-128-TEST ciphertext: 4 bytes, encrypted_key: 8 bytes, iv: 3 bytes>"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#to_hash" do
|
37
|
+
it "has string keys, binary values base64 encoded" do
|
38
|
+
enigma.to_hash.tap do |h|
|
39
|
+
h["cipher"].must_equal "AES-128-TEST"
|
40
|
+
h["ciphertext"].must_equal bin64("ct")
|
41
|
+
h["encrypted_key"].must_equal bin64("ek")
|
42
|
+
h["iv"].must_equal bin64("iv")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
it "has US-ASCII keys and values" do
|
46
|
+
enigma.to_hash.tap do |h|
|
47
|
+
h.each do |key, value|
|
48
|
+
key.encoding.name.must_equal "US-ASCII"
|
49
|
+
key.valid_encoding?.must_equal true
|
50
|
+
value.encoding.name.must_equal "US-ASCII"
|
51
|
+
value.valid_encoding?.must_equal true
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe ".from_hash" do
|
58
|
+
it "accepts hash with string keys for easy deserialization" do
|
59
|
+
hash = {
|
60
|
+
"cipher" => "AES-128-TEST",
|
61
|
+
"ciphertext" => bin64("test_ct"),
|
62
|
+
"encrypted_key" => bin64("test_ek"),
|
63
|
+
"iv" => bin64("test_iv")
|
64
|
+
}
|
65
|
+
Strongroom::Enigma.from_hash(hash).tap do |e|
|
66
|
+
e.cipher.must_equal "AES-128-TEST"
|
67
|
+
e.ciphertext.must_equal binary("test_ct")
|
68
|
+
e.encrypted_key.must_equal binary("test_ek")
|
69
|
+
e.iv.must_equal binary("test_iv")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "#serialize" do
|
75
|
+
def s; enigma.serialize; end
|
76
|
+
it "returns a String" do
|
77
|
+
s.must_be_kind_of String
|
78
|
+
end
|
79
|
+
it "is not empty" do
|
80
|
+
s.wont_be_empty
|
81
|
+
end
|
82
|
+
it "has is valid US-ASCII" do
|
83
|
+
s.encoding.name.must_equal "US-ASCII"
|
84
|
+
s.valid_encoding?.must_equal true
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe ".deserialize" do
|
89
|
+
it "correctly deserializes output from #serialize" do
|
90
|
+
Strongroom::Enigma.deserialize(enigma.serialize).tap do |e|
|
91
|
+
e.cipher.must_equal "AES-128-TEST"
|
92
|
+
e.ciphertext.must_equal binary("ct")
|
93
|
+
e.encrypted_key.must_equal binary("ek")
|
94
|
+
e.iv.must_equal binary("iv")
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
describe Strongroom::HasRsaKey do
|
4
|
+
|
5
|
+
klass = Class.new do
|
6
|
+
include Strongroom::HasRsaKey
|
7
|
+
def initialize key
|
8
|
+
has_rsa_key key
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
it "passes through non-String objects" do
|
13
|
+
key = MiniTest::Mock.new
|
14
|
+
klass.new(key).send(:rsa_key).must_equal key
|
15
|
+
end
|
16
|
+
|
17
|
+
it "loads RSA key from filepath" do
|
18
|
+
key = fixture_path("public_key")
|
19
|
+
klass.new(key).send(:rsa_key).must_be_kind_of OpenSSL::PKey::RSA
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
describe Strongroom do
|
4
|
+
|
5
|
+
describe "round trip encryption" do
|
6
|
+
|
7
|
+
# generate a key pair in memory
|
8
|
+
def key_pair
|
9
|
+
Struct.new(:private, :public).new(
|
10
|
+
pk = OpenSSL::PKey::RSA.new(1024),
|
11
|
+
pk.public_key
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
{ short: 16, medium: 16 * 1024, long: 2**20 }.each do |name, length|
|
16
|
+
|
17
|
+
describe "with #{name} input (#{length} bytes)" do
|
18
|
+
before do
|
19
|
+
@input = "abcd1234" * (length / 8)
|
20
|
+
@input.length.must_equal length
|
21
|
+
end
|
22
|
+
|
23
|
+
it "round-trips with real Cipher and RSA keys" do
|
24
|
+
key = key_pair
|
25
|
+
enigma = Strongroom::Encryptor.new(key.public).encrypt(@input)
|
26
|
+
output = Strongroom::Decryptor.new(key.private).decrypt(enigma)
|
27
|
+
output.must_equal @input
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
data/strongroom.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/strongroom/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Paul Annesley"]
|
6
|
+
gem.email = ["paul@annesley.cc"]
|
7
|
+
gem.description = %q{Strong public-key encryption for arbitrary length data.}
|
8
|
+
gem.summary = %q{Strongroom uses RSA and AES encryption from Ruby's OpenSSL bindings to encrypt arbitrary length data with a public key.}
|
9
|
+
gem.homepage = "https://github.com/learnable/strongroom"
|
10
|
+
|
11
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
12
|
+
gem.files = `git ls-files`.split("\n")
|
13
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
+
gem.name = "strongroom"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Strongroom::VERSION
|
17
|
+
|
18
|
+
gem.required_ruby_version = ">= 1.9.2"
|
19
|
+
|
20
|
+
gem.add_development_dependency "rake"
|
21
|
+
gem.add_development_dependency "simplecov"
|
22
|
+
gem.add_development_dependency "sdoc"
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: strongroom
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Paul Annesley
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-09-19 00:00:00 +10:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rake
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :development
|
32
|
+
version_requirements: *id001
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: simplecov
|
35
|
+
prerelease: false
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 0
|
43
|
+
version: "0"
|
44
|
+
type: :development
|
45
|
+
version_requirements: *id002
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: sdoc
|
48
|
+
prerelease: false
|
49
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
segments:
|
55
|
+
- 0
|
56
|
+
version: "0"
|
57
|
+
type: :development
|
58
|
+
version_requirements: *id003
|
59
|
+
description: Strong public-key encryption for arbitrary length data.
|
60
|
+
email:
|
61
|
+
- paul@annesley.cc
|
62
|
+
executables: []
|
63
|
+
|
64
|
+
extensions: []
|
65
|
+
|
66
|
+
extra_rdoc_files: []
|
67
|
+
|
68
|
+
files:
|
69
|
+
- .gitignore
|
70
|
+
- .travis.yml
|
71
|
+
- Gemfile
|
72
|
+
- README.md
|
73
|
+
- Rakefile
|
74
|
+
- lib/strongroom.rb
|
75
|
+
- lib/strongroom/decryptor.rb
|
76
|
+
- lib/strongroom/encryptor.rb
|
77
|
+
- lib/strongroom/enigma.rb
|
78
|
+
- lib/strongroom/has_rsa_key.rb
|
79
|
+
- lib/strongroom/version.rb
|
80
|
+
- spec/fixtures/private_key
|
81
|
+
- spec/fixtures/public_key
|
82
|
+
- spec/spec_helper.rb
|
83
|
+
- spec/strongroom_decryptor_spec.rb
|
84
|
+
- spec/strongroom_encryptor_spec.rb
|
85
|
+
- spec/strongroom_enigma_spec.rb
|
86
|
+
- spec/strongroom_has_rsa_key_spec.rb
|
87
|
+
- spec/strongroom_spec.rb
|
88
|
+
- strongroom.gemspec
|
89
|
+
has_rdoc: true
|
90
|
+
homepage: https://github.com/learnable/strongroom
|
91
|
+
licenses: []
|
92
|
+
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options: []
|
95
|
+
|
96
|
+
require_paths:
|
97
|
+
- lib
|
98
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
99
|
+
none: false
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
segments:
|
104
|
+
- 1
|
105
|
+
- 9
|
106
|
+
- 2
|
107
|
+
version: 1.9.2
|
108
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
109
|
+
none: false
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
segments:
|
114
|
+
- 0
|
115
|
+
version: "0"
|
116
|
+
requirements: []
|
117
|
+
|
118
|
+
rubyforge_project:
|
119
|
+
rubygems_version: 1.3.7
|
120
|
+
signing_key:
|
121
|
+
specification_version: 3
|
122
|
+
summary: Strongroom uses RSA and AES encryption from Ruby's OpenSSL bindings to encrypt arbitrary length data with a public key.
|
123
|
+
test_files:
|
124
|
+
- spec/fixtures/private_key
|
125
|
+
- spec/fixtures/public_key
|
126
|
+
- spec/spec_helper.rb
|
127
|
+
- spec/strongroom_decryptor_spec.rb
|
128
|
+
- spec/strongroom_encryptor_spec.rb
|
129
|
+
- spec/strongroom_enigma_spec.rb
|
130
|
+
- spec/strongroom_has_rsa_key_spec.rb
|
131
|
+
- spec/strongroom_spec.rb
|