strongroom 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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)
|
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
|