bequest 0.0.3
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 +3 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +68 -0
- data/Rakefile +1 -0
- data/bequest.gemspec +20 -0
- data/lib/bequest.rb +3 -0
- data/lib/bequest/data.rb +132 -0
- data/lib/bequest/license.rb +38 -0
- data/lib/bequest/version.rb +3 -0
- data/samples/hello.exe +0 -0
- data/samples/lic.dat +0 -0
- data/samples/secret.txt +44 -0
- data/samples/tampered_checksum_lic.dat +0 -0
- data/samples/tampered_lic.dat +0 -0
- data/spec/helper.rb +8 -0
- data/spec/license_spec.rb +279 -0
- metadata +113 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm --create use 1.8.7@bequest
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Andy White
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# Bequest
|
2
|
+
|
3
|
+
There might be times when you (the bequestor) want to provide data to a third party (the bequestee) for a limited time and/or only on a specific machine. Bequest enables password, MAC address and expiry-based protection of data via a single, binary, encrypted license file.
|
4
|
+
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem 'bequest'
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
Or install it yourself as:
|
17
|
+
|
18
|
+
$ gem install bequest
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
The bequestor would run:
|
23
|
+
|
24
|
+
Bequest::License.create('path/to/source/data', 'path/to/out/file',
|
25
|
+
:expires_at => Time.now + 1.year,
|
26
|
+
:password => 'secret word or phrase',
|
27
|
+
:mac_addr => 'bequestee:MAC:address:for:example')
|
28
|
+
|
29
|
+
Parameters:
|
30
|
+
|
31
|
+
1. Source data file - this is the secret data you want made securely available to the client, it can be plain text or binary
|
32
|
+
2. Out file - the license file that will be created
|
33
|
+
|
34
|
+
Options (at least ONE of :password or :mac_addr must be set):
|
35
|
+
|
36
|
+
* :expires_at - time as a Ruby Time object, no expiry if omitted or set to nil
|
37
|
+
* :password - this can be any word or phrase and will be prompted for (if set and not supplied as an option) when the license is loaded
|
38
|
+
* :mac_addr - if set this will be read from the local machine when the bequestee loads the license if not supplied as an option
|
39
|
+
|
40
|
+
The serialised, binary license file contains a main checksum and a body. The body is composed of:
|
41
|
+
|
42
|
+
* The encrypted expiry time
|
43
|
+
* Password and MAC address booleans
|
44
|
+
* The compressed, encrypted source data
|
45
|
+
* The initialisation vector
|
46
|
+
|
47
|
+
The main checksum is of the body as a joined array, before it was serialised in the license file. It is used to validate the integrity of the license file.
|
48
|
+
|
49
|
+
The client would run:
|
50
|
+
|
51
|
+
lic, data = Bequest::License.load('path/to/lic/file',
|
52
|
+
:password => 'whatever', :mac_addr => 'whatever')
|
53
|
+
|
54
|
+
The password will be prompted for if set and *:password* not supplied. The first MAC address of the local machine will be used if set and *:mac_addr* not supplied. *data* will be set to nil if authentication fails or it has expired. *lic* will always be populated regardless of success and may be queried as follows:
|
55
|
+
|
56
|
+
lic.valid? # => true or false
|
57
|
+
lic.expired? # => true or false
|
58
|
+
lic.expires_at # => Ruby Time object
|
59
|
+
lic.status # => :ok, :expired, :unauthorized, or :tampered
|
60
|
+
|
61
|
+
|
62
|
+
## Contributing
|
63
|
+
|
64
|
+
1. Fork it
|
65
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
66
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
67
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
68
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bequest.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'bequest/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "bequest"
|
8
|
+
s.version = Bequest::VERSION
|
9
|
+
s.authors = ["Andy White"]
|
10
|
+
s.email = ["andy@wireworldmedia.co.uk"]
|
11
|
+
s.description = %q{License secure data}
|
12
|
+
s.summary = %q{Bequest enables password, MAC address and expiry-based validation and secure data provision via a single license file.}
|
13
|
+
s.homepage = ""
|
14
|
+
s.files = `git ls-files`.split($/)
|
15
|
+
s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
16
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
s.add_runtime_dependency "macaddr", ">= 1.6.1"
|
19
|
+
s.add_development_dependency "rspec", ">= 0"
|
20
|
+
end
|
data/lib/bequest.rb
ADDED
data/lib/bequest/data.rb
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'digest/sha2'
|
3
|
+
require 'digest/md5'
|
4
|
+
require 'zlib'
|
5
|
+
require 'macaddr'
|
6
|
+
|
7
|
+
module Bequest
|
8
|
+
class Data
|
9
|
+
def initialize(secret_data, opts = {})
|
10
|
+
opts = {:expires_at => nil, :password => '', :mac_addr => ''}.merge(opts)
|
11
|
+
compressed = Zlib::Deflate.deflate(secret_data)
|
12
|
+
key = key(opts[:password], opts[:mac_addr])
|
13
|
+
iv = Digest::MD5.hexdigest(rand.to_s)
|
14
|
+
encrypted_data = encrypt(compressed, key, iv)
|
15
|
+
encrypted_expires_at = opts[:expires_at] ? encrypt(opts[:expires_at].to_i.to_s, key, iv) : nil
|
16
|
+
|
17
|
+
body = [encrypted_expires_at, opts[:password].any?, opts[:mac_addr].any?, encrypted_data, iv]
|
18
|
+
checksum = Digest::MD5.hexdigest(body.join)
|
19
|
+
@data = [checksum, body]
|
20
|
+
end
|
21
|
+
|
22
|
+
def dump(path)
|
23
|
+
FileUtils.mkdir_p(File.dirname(path))
|
24
|
+
File.open(path, "w") { |f| f.write(Marshal::dump(self)) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def unpack(password, mac_addr)
|
28
|
+
password = prompt_if_required(password)
|
29
|
+
mac_addr = get_if_required(mac_addr)
|
30
|
+
|
31
|
+
if Digest::MD5.hexdigest(body.join) == checksum
|
32
|
+
compressed = decrypt(encrypted_data, key(password, mac_addr), iv)
|
33
|
+
|
34
|
+
begin
|
35
|
+
data = Zlib::Inflate.inflate(compressed)
|
36
|
+
|
37
|
+
if encrypted_expires_at
|
38
|
+
expires_at = Time.at(decrypt(encrypted_expires_at, key(password, mac_addr), iv).to_i)
|
39
|
+
|
40
|
+
if expires_at.to_i < Time.now.to_i
|
41
|
+
[nil, :expired, expires_at]
|
42
|
+
else
|
43
|
+
[data, :ok, expires_at]
|
44
|
+
end
|
45
|
+
else
|
46
|
+
[data, :ok, nil]
|
47
|
+
end
|
48
|
+
rescue
|
49
|
+
[nil, :unauthorized, nil]
|
50
|
+
end
|
51
|
+
else
|
52
|
+
[nil, :tampered, nil]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def checksum
|
59
|
+
@data[0]
|
60
|
+
end
|
61
|
+
|
62
|
+
def body
|
63
|
+
@data[1]
|
64
|
+
end
|
65
|
+
|
66
|
+
def encrypted_expires_at
|
67
|
+
body[0]
|
68
|
+
end
|
69
|
+
|
70
|
+
def need_password?
|
71
|
+
body[1]
|
72
|
+
end
|
73
|
+
|
74
|
+
def need_mac_addr?
|
75
|
+
body[2]
|
76
|
+
end
|
77
|
+
|
78
|
+
def encrypted_data
|
79
|
+
body[3]
|
80
|
+
end
|
81
|
+
|
82
|
+
def iv
|
83
|
+
body[4]
|
84
|
+
end
|
85
|
+
|
86
|
+
def prompt_if_required(password)
|
87
|
+
if need_password? && password.nil?
|
88
|
+
STDOUT.puts "Password: "
|
89
|
+
STDOUT.flush
|
90
|
+
gets.chomp
|
91
|
+
else
|
92
|
+
password
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def get_if_required(mac_addr)
|
97
|
+
if need_mac_addr? && mac_addr.nil?
|
98
|
+
Mac.addr
|
99
|
+
else
|
100
|
+
mac_addr
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def key(password, mac_addr)
|
105
|
+
sha256 = Digest::SHA2.new(256)
|
106
|
+
sha256.digest(
|
107
|
+
'ZggthtmuGfDWy4D' + (password || '') + 'gt5gIrfMcgyb8ii' + (mac_addr || '') + 'C1pq3gi65SX2ckx'
|
108
|
+
)
|
109
|
+
end
|
110
|
+
|
111
|
+
def encrypt(data, key, iv)
|
112
|
+
aes = OpenSSL::Cipher.new("AES-256-CFB")
|
113
|
+
aes.encrypt
|
114
|
+
aes.key = key
|
115
|
+
aes.iv = iv
|
116
|
+
aes.update(data) + aes.final
|
117
|
+
end
|
118
|
+
|
119
|
+
def decrypt(data, key, iv)
|
120
|
+
aes = OpenSSL::Cipher.new("AES-256-CFB")
|
121
|
+
aes.decrypt
|
122
|
+
aes.key = key
|
123
|
+
aes.iv = iv
|
124
|
+
|
125
|
+
begin
|
126
|
+
aes.update(data) + aes.final
|
127
|
+
rescue
|
128
|
+
false
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Bequest
|
2
|
+
class License
|
3
|
+
attr_reader :expires_at, :status
|
4
|
+
|
5
|
+
def initialize(status, expires_at)
|
6
|
+
@status = status
|
7
|
+
@expires_at = expires_at
|
8
|
+
end
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def create(data_path, out_path, opts = {})
|
12
|
+
if (opts[:password]||'').any? || (opts[:mac_addr]||'').any?
|
13
|
+
Data.new(File.read(data_path), opts).dump(out_path)
|
14
|
+
else
|
15
|
+
puts "At least ONE of password or mac_addr required"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def load(lic_file_path, opts = {})
|
20
|
+
begin
|
21
|
+
data = Marshal::load(File.read(lic_file_path))
|
22
|
+
original_data, status, expires_at = data.unpack(opts[:password], opts[:mac_addr])
|
23
|
+
[self.new(status, expires_at), original_data]
|
24
|
+
rescue
|
25
|
+
[self.new(:tampered, nil), nil]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def valid?
|
31
|
+
@status == :ok
|
32
|
+
end
|
33
|
+
|
34
|
+
def expired?
|
35
|
+
expires_at ? expires_at < Time.now : nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/samples/hello.exe
ADDED
Binary file
|
data/samples/lic.dat
ADDED
Binary file
|
data/samples/secret.txt
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
This is
|
2
|
+
top secret.
|
3
|
+
|
4
|
+
...and here's a secret camel:
|
5
|
+
|
6
|
+
__
|
7
|
+
.--. .' `.
|
8
|
+
.' . :\ / : L
|
9
|
+
F :\ / . : | .-._
|
10
|
+
/ : \/ J .' ___\
|
11
|
+
J : / : : L /--' ``.
|
12
|
+
F : J | .<'.o. `-'>
|
13
|
+
/ J L \_>. .--w)
|
14
|
+
J / \_/| . `-__|
|
15
|
+
F / ` -' /|)
|
16
|
+
| : J ' |
|
17
|
+
.' ': | . : \
|
18
|
+
/ J : |L
|
19
|
+
F | \ ||
|
20
|
+
F . | : |
|
21
|
+
F | ; . : : F
|
22
|
+
/ | : J
|
23
|
+
J J ) ; F
|
24
|
+
| L / .:' J
|
25
|
+
.-'F: L ./ :: : . F
|
26
|
+
`-'F: .\ `:.J :::. J
|
27
|
+
J ::\ `:| |::::\ |
|
28
|
+
J |:`. J :`:::\ F
|
29
|
+
L :':/ \ `-`. \ : `:::| .-'
|
30
|
+
| / L >--\ :::|`. .-'
|
31
|
+
J J | | L . :::: :`, /
|
32
|
+
L F J ) | >:: : /
|
33
|
+
| J L F \ .-.:' . /
|
34
|
+
): | J / `- | | .--'
|
35
|
+
/ | |: J L J J )
|
36
|
+
L | |: | L F| /
|
37
|
+
\: J \: L \ / L |
|
38
|
+
L | \ | F| | )
|
39
|
+
J F \ J J | |J
|
40
|
+
L| \ \ | | | L
|
41
|
+
J L \ \ F \ F |
|
42
|
+
L\ \ \ J | J L
|
43
|
+
/__\_________)_`._)_ |_/ \_____
|
44
|
+
"" `"""
|
Binary file
|
Binary file
|
data/spec/helper.rb
ADDED
@@ -0,0 +1,279 @@
|
|
1
|
+
require 'bequest'
|
2
|
+
require 'helper'
|
3
|
+
|
4
|
+
module Bequest
|
5
|
+
describe License do
|
6
|
+
before(:each) do
|
7
|
+
set_sandbox
|
8
|
+
@lic_file = './sandbox/lic.dat'
|
9
|
+
@tampered_lic_file = './samples/tampered_lic.dat'
|
10
|
+
@tampered_checksum_lic_file = './samples/tampered_checksum_lic.dat'
|
11
|
+
@data_file = './samples/secret.txt'
|
12
|
+
@soon = Time.now + 10
|
13
|
+
@just_passed = Time.now - 10
|
14
|
+
@password = 'secret'
|
15
|
+
@mac_addr = 'mac:addr'
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "Bequester makes a new license" do
|
19
|
+
before(:each) do
|
20
|
+
License.create(@data_file, @lic_file, :expires_at => @soon, :password => @password)
|
21
|
+
@lic, data = License.load(@lic_file, :password => @password)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "creates a file" do
|
25
|
+
File.exist?(@lic_file).should be_true
|
26
|
+
end
|
27
|
+
|
28
|
+
it "has a correct expiry time" do
|
29
|
+
@lic.expires_at.to_i.should == @soon.to_i
|
30
|
+
end
|
31
|
+
|
32
|
+
it "requires at least ONE of password or mac_addr" do
|
33
|
+
@lic = License.create(@data_file, @lic_file)
|
34
|
+
@lic.should be_nil
|
35
|
+
end
|
36
|
+
|
37
|
+
it "requires at least ONE of password or mac_addr to have a length" do
|
38
|
+
@lic = License.create(@data_file, @lic_file, :password => '')
|
39
|
+
@lic.should be_nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "Bequestee loads an 'in date', password protected license file" do
|
44
|
+
before(:each) do
|
45
|
+
License.create(@data_file, @lic_file, :expires_at => @soon, :password => @password)
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "Correct password supplied" do
|
49
|
+
before(:each) do
|
50
|
+
@lic, @data = License.load(@lic_file, :password => @password)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should be a license" do
|
54
|
+
@lic.class.should == License
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should be valid" do
|
58
|
+
@lic.valid?.should be_true
|
59
|
+
end
|
60
|
+
|
61
|
+
it "status should be OK" do
|
62
|
+
@lic.status.should == :ok
|
63
|
+
end
|
64
|
+
|
65
|
+
it "has a correct expiry time" do
|
66
|
+
@lic.expires_at.to_i.should == @soon.to_i
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should not be expired" do
|
70
|
+
@lic.expired?.should == false
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should yield original data" do
|
74
|
+
@data.should == File.read(@data_file)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "Wrong password supplied" do
|
79
|
+
before(:each) do
|
80
|
+
@lic, @data = License.load(@lic_file, :password => 'plainly wrong')
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should not be valid" do
|
84
|
+
@lic.valid?.should be_false
|
85
|
+
end
|
86
|
+
|
87
|
+
it "status should be unauthorized" do
|
88
|
+
@lic.status.should == :unauthorized
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should have a nil expiry time" do
|
92
|
+
@lic.expires_at.should be_nil
|
93
|
+
end
|
94
|
+
|
95
|
+
it "expired? should be nil" do
|
96
|
+
@lic.expired?.should be_nil
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should yield no data" do
|
100
|
+
@data.should be_nil
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe "No password supplied" do
|
105
|
+
it "should prompt" do
|
106
|
+
STDOUT.should_receive(:puts).with("Password: ")
|
107
|
+
@lic, @data = License.load(@lic_file)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe "Bequestee loads an expired, password protected license file" do
|
113
|
+
before(:each) do
|
114
|
+
lic = License.create(@data_file, @lic_file, :expires_at => @just_passed, :password => @password)
|
115
|
+
@lic, @data = License.load(@lic_file, :password => @password)
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should not be valid" do
|
119
|
+
@lic.valid?.should be_false
|
120
|
+
end
|
121
|
+
|
122
|
+
it "status should be expired" do
|
123
|
+
@lic.status.should == :expired
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should have correct expiry time" do
|
127
|
+
@lic.expires_at.to_i.should == @just_passed.to_i
|
128
|
+
end
|
129
|
+
|
130
|
+
it "expired? should be true" do
|
131
|
+
@lic.expired?.should be_true
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should yield no data" do
|
135
|
+
@data.should be_nil
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe "Bequestee loads a password protected license file with a tampered body" do
|
140
|
+
before(:each) do
|
141
|
+
@lic, @data = License.load(@tampered_lic_file, :password => @password)
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should not be valid" do
|
145
|
+
@lic.valid?.should be_false
|
146
|
+
end
|
147
|
+
|
148
|
+
it "status should be tampered" do
|
149
|
+
@lic.status.should == :tampered
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should have nil expiry time" do
|
153
|
+
@lic.expires_at.should be_nil
|
154
|
+
end
|
155
|
+
|
156
|
+
it "expired? should be nil" do
|
157
|
+
@lic.expired?.should be_nil
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should yield no data" do
|
161
|
+
@data.should be_nil
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe "Bequestee loads a password protected license file with a tampered checksum" do
|
166
|
+
before(:each) do
|
167
|
+
@lic, @data = License.load(@tampered_checksum_lic_file, :password => @password)
|
168
|
+
end
|
169
|
+
|
170
|
+
it "should not be valid" do
|
171
|
+
@lic.valid?.should be_false
|
172
|
+
end
|
173
|
+
|
174
|
+
it "status should be tampered" do
|
175
|
+
@lic.status.should == :tampered
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should have nil expiry time" do
|
179
|
+
@lic.expires_at.should be_nil
|
180
|
+
end
|
181
|
+
|
182
|
+
it "expired? should be nil" do
|
183
|
+
@lic.expired?.should be_nil
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should yield no data" do
|
187
|
+
@data.should be_nil
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
describe "Bequestee loads an imortal, password protected license file" do
|
192
|
+
before(:each) do
|
193
|
+
lic = License.create(@data_file, @lic_file, :password => @password)
|
194
|
+
@lic, @data = License.load(@lic_file, :password => @password)
|
195
|
+
end
|
196
|
+
|
197
|
+
it "should be valid" do
|
198
|
+
@lic.valid?.should be_true
|
199
|
+
end
|
200
|
+
|
201
|
+
it "status should be ok" do
|
202
|
+
@lic.status.should == :ok
|
203
|
+
end
|
204
|
+
|
205
|
+
it "should have nil expiry time" do
|
206
|
+
@lic.expires_at.should be_nil
|
207
|
+
end
|
208
|
+
|
209
|
+
it "expired? should be nil" do
|
210
|
+
@lic.expired?.should be_nil
|
211
|
+
end
|
212
|
+
|
213
|
+
it "should yield original data" do
|
214
|
+
@data.should == File.read(@data_file)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
describe "Bequestee loads an imortal, MAC protected (for bequestee machine) license file" do
|
219
|
+
before(:each) do
|
220
|
+
lic = License.create(@data_file, @lic_file, :mac_addr => Mac.addr)
|
221
|
+
end
|
222
|
+
|
223
|
+
describe "no MAC supplied" do
|
224
|
+
before(:each) do
|
225
|
+
@lic, @data = License.load(@lic_file)
|
226
|
+
end
|
227
|
+
|
228
|
+
it "should be valid" do
|
229
|
+
@lic.valid?.should be_true
|
230
|
+
end
|
231
|
+
|
232
|
+
it "status should be ok" do
|
233
|
+
@lic.status.should == :ok
|
234
|
+
end
|
235
|
+
|
236
|
+
it "should yield original data" do
|
237
|
+
@data.should == File.read(@data_file)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
describe "Wrong MAC supplied" do
|
242
|
+
before(:each) do
|
243
|
+
@lic, @data = License.load(@lic_file, :mac_addr => 'very:wrong:mac:addr:indeed')
|
244
|
+
end
|
245
|
+
|
246
|
+
it "should not be valid" do
|
247
|
+
@lic.valid?.should be_false
|
248
|
+
end
|
249
|
+
|
250
|
+
it "status should be unauthorized" do
|
251
|
+
@lic.status.should == :unauthorized
|
252
|
+
end
|
253
|
+
|
254
|
+
it "should have a nil expiry time" do
|
255
|
+
@lic.expires_at.should be_nil
|
256
|
+
end
|
257
|
+
|
258
|
+
it "expired? should be nil" do
|
259
|
+
@lic.expired?.should be_nil
|
260
|
+
end
|
261
|
+
|
262
|
+
it "should yield no data" do
|
263
|
+
@data.should be_nil
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
describe "Bequestee loads a binary file" do
|
269
|
+
before(:each) do
|
270
|
+
License.create('./samples/hello.exe', @lic_file, :password => @password)
|
271
|
+
@lic, @data = License.load(@lic_file, :password => @password)
|
272
|
+
end
|
273
|
+
|
274
|
+
it "binary data matches original" do
|
275
|
+
@data == open('./samples/hello.exe', "rb") {|io| io.read }
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bequest
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 25
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 3
|
10
|
+
version: 0.0.3
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Andy White
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2013-02-21 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: macaddr
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 13
|
29
|
+
segments:
|
30
|
+
- 1
|
31
|
+
- 6
|
32
|
+
- 1
|
33
|
+
version: 1.6.1
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rspec
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 3
|
45
|
+
segments:
|
46
|
+
- 0
|
47
|
+
version: "0"
|
48
|
+
type: :development
|
49
|
+
version_requirements: *id002
|
50
|
+
description: License secure data
|
51
|
+
email:
|
52
|
+
- andy@wireworldmedia.co.uk
|
53
|
+
executables: []
|
54
|
+
|
55
|
+
extensions: []
|
56
|
+
|
57
|
+
extra_rdoc_files: []
|
58
|
+
|
59
|
+
files:
|
60
|
+
- .gitignore
|
61
|
+
- .rvmrc
|
62
|
+
- Gemfile
|
63
|
+
- LICENSE.txt
|
64
|
+
- README.md
|
65
|
+
- Rakefile
|
66
|
+
- bequest.gemspec
|
67
|
+
- lib/bequest.rb
|
68
|
+
- lib/bequest/data.rb
|
69
|
+
- lib/bequest/license.rb
|
70
|
+
- lib/bequest/version.rb
|
71
|
+
- samples/hello.exe
|
72
|
+
- samples/lic.dat
|
73
|
+
- samples/secret.txt
|
74
|
+
- samples/tampered_checksum_lic.dat
|
75
|
+
- samples/tampered_lic.dat
|
76
|
+
- spec/helper.rb
|
77
|
+
- spec/license_spec.rb
|
78
|
+
homepage: ""
|
79
|
+
licenses: []
|
80
|
+
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
hash: 3
|
92
|
+
segments:
|
93
|
+
- 0
|
94
|
+
version: "0"
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
hash: 3
|
101
|
+
segments:
|
102
|
+
- 0
|
103
|
+
version: "0"
|
104
|
+
requirements: []
|
105
|
+
|
106
|
+
rubyforge_project:
|
107
|
+
rubygems_version: 1.8.25
|
108
|
+
signing_key:
|
109
|
+
specification_version: 3
|
110
|
+
summary: Bequest enables password, MAC address and expiry-based validation and secure data provision via a single license file.
|
111
|
+
test_files:
|
112
|
+
- spec/helper.rb
|
113
|
+
- spec/license_spec.rb
|