owlet 0.1.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 ADDED
@@ -0,0 +1,9 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ spec/fixtures/*.alulaextz
6
+ spec/fixtures/*.alulathmz
7
+ /spec/reports/
8
+ /spec/fixtures/fixture.alulaextz
9
+ /spec/fixtures/fixture.alulathmz
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in owlet.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,6 @@
1
+ guard 'rspec', :version => 2, :cli => "--color --format documentation" do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
4
+ watch(%r{^lib/(.+)/(.+)\.rb$}) { |m| "spec/#{m[1]}/#{m[2]}_spec.rb" }
5
+ watch('spec/spec_helper.rb') { "spec" }
6
+ end
data/README.md ADDED
@@ -0,0 +1,17 @@
1
+ # Owlet
2
+
3
+ Owlet is a package system for Alula extensions and themes. Using custom packages for extensions
4
+ gives benefits of easier distribution, downloading and management. Using single file packages
5
+ it is easy to upload required extensions and themes directly to the server from your computer.
6
+
7
+ In addition, all Owlets are digitally signed to make sure that package is not altered in any ways.
8
+
9
+ ## Technical
10
+
11
+ Basically Owlets are simple ZIP files which as `_Signature` file as addition.
12
+ Signature file is simple YAML file, which contains signatures in base64 encoded
13
+ for all files that are in package, as well public certificate that was used
14
+ in signing process.
15
+
16
+ Owlets can be signed more than once, every additional signing round adds more signatures and
17
+ public key. While verifying packages, all signatures must match to public keys.
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:spec) do |spec|
5
+ spec.pattern = FileList['spec/**/*_spec.rb']
6
+ end
7
+
8
+ require 'rubygems'
9
+ require 'ci/reporter/rake/rspec'
data/bin/owlet ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
3
+
4
+ require 'owlet/cli'
5
+
6
+ Owlet::CLI.start
data/lib/owlet/cli.rb ADDED
@@ -0,0 +1,38 @@
1
+ require 'thor'
2
+ require 'owlet'
3
+
4
+ module Owlet
5
+ class CLI < Thor
6
+ # Signing operations
7
+ desc "sign PACKAGE", "Signs Owlet package with supplied key."
8
+ method_option :key, :required => true, :description => "Private and public key to be used for signing. Must be in PKCS12 format."
9
+ def sign(package)
10
+ Owlet::Signer.sign_package(package, options[:key])
11
+ end
12
+
13
+ desc "verify PACKAGE", "Verifies Alula extension or theme"
14
+ def verify(package)
15
+ begin
16
+ Owlet::Signer.verify_package(package)
17
+ puts "Verify OK."
18
+ rescue
19
+ puts "Verify FAILED."
20
+ end
21
+ end
22
+
23
+ # Packaking operations
24
+ desc "package DIRECTORY", "Create owlet package from given directory."
25
+ method_option :key
26
+ def package(dir)
27
+ pkg = Owlet::Package.create_from_dir(dir)
28
+ if (options[:key] and pkg)
29
+ Owlet::Signer.sign_package(pkg, options[:key])
30
+ end
31
+ end
32
+
33
+ desc "extract PACKAGE [DIR]", "Extracts contents of given package, optionally to given directory"
34
+ def extract(package, dir = ".")
35
+ Owlet::Package.extract(package, dir)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,35 @@
1
+ require 'zip/zip'
2
+
3
+ module Owlet
4
+ class Package
5
+
6
+ def self.create(args = {})
7
+ source = args[:source]
8
+
9
+ # Validate source
10
+ unless File.directory?(source)
11
+ raise "Invalid source directory."
12
+ end
13
+
14
+ ext = File.extname(source)
15
+ dest_ext = case ext[1..-1]
16
+ when "alulaextension"
17
+ "alulaextz"
18
+ when "alulatheme"
19
+ "alulathmz"
20
+ else
21
+ raise "Unknown owlet type."
22
+ end
23
+ destination = args[:destination] || File.join(File.dirname(source), "#{File.basename(source, ext)}.#{dest_ext}")
24
+
25
+ Zip::ZipFile.open(destination, Zip::ZipFile::CREATE) do |zip|
26
+ Dir.glob("#{source}/**/*").each do |entry|
27
+ zip.add(entry.gsub("#{source}/", ''), entry)
28
+ end
29
+ end
30
+
31
+ # All done
32
+ return destination
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,104 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+
4
+ module Owlet
5
+ class Signer
6
+ # Sign arbitary data using private_key
7
+ # Return Base 64 (RFC 4648) encoded signature
8
+ def self.sign(data, private_key)
9
+ Base64.encode64(private_key.sign(OpenSSL::Digest::SHA256.new, data)).gsub(/\n/, '')
10
+ end
11
+
12
+ # Verify arbitary data against given signature and public key.
13
+ # Signature must be Base64 encoded, RFC 4648 compliat.
14
+ def self.verify(data, signature, public_key)
15
+ public_key.verify(OpenSSL::Digest::SHA256.new, Base64.decode64(signature).gsub(/\n/, ''), data)
16
+ end
17
+
18
+ def self.list_certificates(package)
19
+ # Check that package exists.
20
+ raise "Cannot file package #{package}" unless File.exists?(package)
21
+
22
+ sig_data = Zip::ZipFile.open(package) do |zip|
23
+ entry = zip.find_entry("_Signature")
24
+ unless entry.nil?
25
+ YAML.load(entry.get_input_stream.read)
26
+ else
27
+ {:certificates => []}
28
+ end
29
+ end
30
+
31
+ sig_data[:certificates]
32
+ end
33
+
34
+ # Signs given package with PKCS12 file
35
+ # p12 is either OpenSSL::PKCS!@ or file
36
+ def self.sign_package(package, p12)
37
+ # Get private key
38
+ unless p12.kind_of? OpenSSL::PKCS12
39
+ p12 = OpenSSL::PKCS12.new(File.read(p12))
40
+ end
41
+
42
+ raise "Cannot find package #{package}" unless File.exists?(package)
43
+
44
+ # Initialize signature data
45
+ sig_data = { :signatures => {}, :certificates => []}
46
+
47
+ # Open and sign files
48
+ Zip::ZipFile.open(package) do |zip|
49
+ sig_meta = zip.find_entry("_Signature")
50
+ unless sig_meta.nil?
51
+ sig_data = YAML.load(sig_meta.get_input_stream.read)
52
+ end
53
+
54
+ zip.each do |entry|
55
+ data = entry.get_input_stream.read
56
+ next if data.nil?
57
+
58
+ sig_data[:signatures][entry.name] ||= []
59
+ sig_data[:signatures][entry.name].push sign(data, p12.key)
60
+ end
61
+
62
+ sig_data[:certificates].push p12.certificate.to_pem
63
+
64
+ zip.get_output_stream("_Signature") do |io|
65
+ io.puts sig_data.to_yaml
66
+ end
67
+ end
68
+
69
+ true
70
+ end
71
+
72
+ def self.verify_package(package)
73
+ raise "Cannot find package #{package}" unless File.exists?(package)
74
+
75
+ Zip::ZipFile.open(package) do |zip|
76
+ entry = zip.find_entry("_Signature")
77
+ raise "Package not signed." if entry.nil?
78
+
79
+ sig_data = YAML.load(entry.get_input_stream.read)
80
+ sig_data[:certificates].map! { |c| OpenSSL::X509::Certificate.new(c) }
81
+
82
+ zip.each do |entry|
83
+ next if entry.name == "_Signature"
84
+
85
+ # Check for all certificates
86
+ sig_data[:certificates].each_index do |i|
87
+ data = entry.get_input_stream.read
88
+ next if data.nil?
89
+
90
+ if sig_data[:signatures][entry.name].nil? or sig_data[:signatures][entry.name][i].nil?
91
+ raise "Cannot find signature for #{entry.name}"
92
+ end
93
+
94
+ unless verify(data, sig_data[:signatures][entry.name][i], sig_data[:certificates][i].public_key)
95
+ raise "Signature for #{entry.name} for certicate #{sig_data[:certificates][i].subject} failed."
96
+ end
97
+ end
98
+ end
99
+ end
100
+
101
+ return true
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,3 @@
1
+ module Owlet
2
+ VERSION = "0.1.1"
3
+ end
data/lib/owlet.rb ADDED
@@ -0,0 +1,6 @@
1
+ require "owlet/version"
2
+
3
+ module Owlet
4
+ autoload :Package, 'owlet/package'
5
+ autoload :Signer, 'owlet/signer'
6
+ end
data/owlet.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "owlet/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "owlet"
7
+ s.version = Owlet::VERSION
8
+ s.authors = ["Mikko Kokkonen"]
9
+ s.email = ["mikko@mikian.com"]
10
+ s.homepage = "http://alula.in/owlet"
11
+ s.summary = %q{Owlet is a packaging system for distributing extensions and themes for Alula Engine}
12
+ s.description = %q{Use Owlet library to create and alter and gather information about owlets.}
13
+
14
+ s.rubyforge_project = "owlet"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_dependency 'thor'
22
+ s.add_dependency 'rubyzip'
23
+
24
+ s.add_development_dependency 'rake'
25
+ s.add_development_dependency 'rspec'
26
+ s.add_development_dependency 'guard-rspec'
27
+ s.add_development_dependency 'ci_reporter'
28
+ end
Binary file
@@ -0,0 +1,25 @@
1
+ extension "GoSquared"
2
+
3
+ description "GoSquared - real-time web analytics"
4
+
5
+ configuration do
6
+ desc "Site Token for your site"
7
+ option :token, :type => String
8
+ end
9
+
10
+ analytics :position => :body_bottom, do |c|
11
+ <<<-EOD
12
+ <script type="text/javascript">
13
+ var GoSquared={};
14
+ GoSquared.acct = "#{c.token}";
15
+ (function(w){
16
+ function gs(){
17
+ w._gstc_lt=+(new Date); var d=document;
18
+ var g = d.createElement("script"); g.type = "text/javascript"; g.async = true; g.src = "//d1l6p2sc9645hc.cloudfront.net/tracker.js";
19
+ var s = d.getElementsByTagName("script")[0]; s.parentNode.insertBefore(g, s);
20
+ }
21
+ w.addEventListener?w.addEventListener("load",gs,false):w.attachEvent("onload",gs);
22
+ })(window);
23
+ </script>
24
+ EOD
25
+ end
@@ -0,0 +1,8 @@
1
+ theme "fixture"
2
+
3
+ description "Fixture theme for Alula"
4
+
5
+ configuration do
6
+ desc "Header logo"
7
+ option :header, :type => :image, :size => [960, 80]
8
+ end
@@ -0,0 +1,5 @@
1
+ $background: #efefef;
2
+
3
+ body {
4
+ background-color: $background;
5
+ }
Binary file
File without changes
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe Owlet::Package do
4
+ let(:fixture_path) { fixture_path = File.expand_path(File.join(File.dirname(__FILE__), "..", "fixtures")) }
5
+
6
+ before(:all) do
7
+ %w{fixture.alulaextz fixture.alulathmz}.each do |fixture|
8
+ File.unlink(File.join(fixture_path, fixture)) if File.exists?(File.join(fixture_path, fixture))
9
+ end
10
+ end
11
+
12
+ it "should raise error for invalid source path" do
13
+ lambda { Owlet::Package.create(:source => File.join(fixture_path, "missing_path")) }.should raise_error("Invalid source directory.")
14
+ end
15
+
16
+ it "should create a package from extension" do
17
+ pkg = Owlet::Package.create(:source => File.join(fixture_path, "fixture.alulaextension"))
18
+ pkg.should eq(File.join(fixture_path, "fixture.alulaextz"))
19
+ end
20
+
21
+ it "should create package from theme" do
22
+ pkg = Owlet::Package.create(:source => File.join(fixture_path, "fixture.alulatheme"))
23
+ pkg.should eq(File.join(fixture_path, "fixture.alulathmz"))
24
+ end
25
+ end
@@ -0,0 +1,91 @@
1
+ require 'spec_helper'
2
+
3
+ describe Owlet::Signer do
4
+ let(:fixture_path) { fixture_path = File.expand_path(File.join(File.dirname(__FILE__), "..", "fixtures")) }
5
+
6
+ context "Helper Methods" do
7
+ let(:first_p12) { OpenSSL::PKCS12.new(File.read(File.join(fixture_path, "first.p12"))) }
8
+ let(:pleco_p12) { OpenSSL::PKCS12.new(File.read(File.join(fixture_path, "pleco.p12"))) }
9
+ let(:first_signature) { "lu2zFctrv7ssmcudjTVNoj9xS47GpMsYBxF8tQVhXnpOBXIM/7TuY+ZpHkxUqzgWssJdrUqLpC6t9OoUyLbUOU2HI4aNdmwOHv3XmpuIazmELEz/aCbwuCnwIAw6YtQiF14GQhUqR2A/6LE9ZB3+bKBDuxSMRY9WbRHELjoeXyg=" }
10
+
11
+ it "should sign data" do
12
+ # Load private key
13
+ data = "aaaa/bbbb/cccc"
14
+ signature = Owlet::Signer.sign(data, first_p12.key)
15
+
16
+ signature.should == first_signature
17
+ end
18
+
19
+ it "should verify data" do
20
+ data = "aaaa/bbbb/cccc"
21
+
22
+ Owlet::Signer.verify(data, first_signature, first_p12.certificate.public_key).should be_true
23
+ end
24
+
25
+ it "should not verify invalid data" do
26
+ data = "aaaa/bbbb/cccc/dddd"
27
+
28
+ Owlet::Signer.verify(data, first_signature, first_p12.certificate.public_key).should be_false
29
+ end
30
+
31
+ it "should not verify with wrong public key" do
32
+ data = "aaaa/bbbb/cccc"
33
+
34
+ Owlet::Signer.verify(data, first_signature, pleco_p12.certificate.public_key).should be_false
35
+ end
36
+ end
37
+
38
+ context "Package" do
39
+ let(:first_p12) { OpenSSL::PKCS12.new(File.read(File.join(fixture_path, "first.p12"))) }
40
+ let(:pleco_p12) { OpenSSL::PKCS12.new(File.read(File.join(fixture_path, "pleco.p12"))) }
41
+
42
+ before(:each) do
43
+ # Create package
44
+ File.unlink(File.join(fixture_path, "fixture.alulaextz")) if File.exists?(File.join(fixture_path, "fixture.alulaextz"))
45
+ @pkg = Owlet::Package.create(:source => File.join(fixture_path, "fixture.alulaextension"))
46
+ end
47
+
48
+ it "should have no certificates" do
49
+ signatures = Owlet::Signer.list_certificates(@pkg)
50
+ signatures.should be_empty
51
+ end
52
+
53
+ it "should be signed succesfully" do
54
+ Owlet::Signer.sign_package(@pkg, first_p12).should be_true
55
+ end
56
+
57
+ it "should have one certificate" do
58
+ Owlet::Signer.sign_package(@pkg, first_p12).should be_true
59
+ signatures = Owlet::Signer.list_certificates(@pkg)
60
+ signatures[0].should_not be_nil
61
+ cert = OpenSSL::X509::Certificate.new(signatures[0])
62
+ cert.subject.to_s.should == "/O=Owl Forestry/CN=Alula Extension"
63
+ signatures[1].should be_nil
64
+ end
65
+
66
+ it "should verify signed package" do
67
+ Owlet::Signer.sign_package(@pkg, first_p12).should be_true
68
+ Owlet::Signer.verify_package(@pkg).should be_true
69
+ end
70
+
71
+ it "should signed twice" do
72
+ Owlet::Signer.sign_package(@pkg, first_p12).should be_true
73
+ Owlet::Signer.sign_package(@pkg, pleco_p12).should be_true
74
+ signatures = Owlet::Signer.list_certificates(@pkg)
75
+ signatures[1].should_not be_nil
76
+ cert = OpenSSL::X509::Certificate.new(signatures[1])
77
+ cert.subject.to_s.should == "/O=Owl Forestry/CN=Alula Pleco - Extension"
78
+ end
79
+
80
+ it "should fail with modified package" do
81
+ Owlet::Signer.sign_package(@pkg, first_p12).should be_true
82
+ Zip::ZipFile.open(@pkg) do |zip|
83
+ zip.get_output_stream("init.rb") do |io|
84
+ io.puts "# Fail this thanks."
85
+ end
86
+ end
87
+
88
+ lambda { Owlet::Signer.verify_package(@pkg)}.should raise_error "Signature for init.rb for certicate /O=Owl Forestry/CN=Alula Extension failed."
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,12 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'owlet'
5
+
6
+ # Requires supporting files with custom matchers and macros, etc,
7
+ # in ./support/ and its subdirectories.
8
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
9
+
10
+ RSpec.configure do |config|
11
+
12
+ end
metadata ADDED
@@ -0,0 +1,145 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: owlet
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Mikko Kokkonen
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-08-07 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: thor
16
+ requirement: &70162863022180 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70162863022180
25
+ - !ruby/object:Gem::Dependency
26
+ name: rubyzip
27
+ requirement: &70162863021740 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70162863021740
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ requirement: &70162863021240 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70162863021240
47
+ - !ruby/object:Gem::Dependency
48
+ name: rspec
49
+ requirement: &70162863020600 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70162863020600
58
+ - !ruby/object:Gem::Dependency
59
+ name: guard-rspec
60
+ requirement: &70162863019940 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70162863019940
69
+ - !ruby/object:Gem::Dependency
70
+ name: ci_reporter
71
+ requirement: &70162863019520 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *70162863019520
80
+ description: Use Owlet library to create and alter and gather information about owlets.
81
+ email:
82
+ - mikko@mikian.com
83
+ executables:
84
+ - owlet
85
+ extensions: []
86
+ extra_rdoc_files: []
87
+ files:
88
+ - .gitignore
89
+ - Gemfile
90
+ - Guardfile
91
+ - README.md
92
+ - Rakefile
93
+ - bin/owlet
94
+ - lib/owlet.rb
95
+ - lib/owlet/cli.rb
96
+ - lib/owlet/package.rb
97
+ - lib/owlet/signer.rb
98
+ - lib/owlet/version.rb
99
+ - owlet.gemspec
100
+ - spec/fixtures/first.p12
101
+ - spec/fixtures/fixture.alulaextension/init.rb
102
+ - spec/fixtures/fixture.alulatheme/init.rb
103
+ - spec/fixtures/fixture.alulatheme/javascripts/application.js.coffeescript
104
+ - spec/fixtures/fixture.alulatheme/stylesheets/application.css.scss
105
+ - spec/fixtures/pleco.p12
106
+ - spec/owlet/cli_spec.rb
107
+ - spec/owlet/package_spec.rb
108
+ - spec/owlet/signer_spec.rb
109
+ - spec/spec_helper.rb
110
+ homepage: http://alula.in/owlet
111
+ licenses: []
112
+ post_install_message:
113
+ rdoc_options: []
114
+ require_paths:
115
+ - lib
116
+ required_ruby_version: !ruby/object:Gem::Requirement
117
+ none: false
118
+ requirements:
119
+ - - ! '>='
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ required_rubygems_version: !ruby/object:Gem::Requirement
123
+ none: false
124
+ requirements:
125
+ - - ! '>='
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ requirements: []
129
+ rubyforge_project: owlet
130
+ rubygems_version: 1.8.6
131
+ signing_key:
132
+ specification_version: 3
133
+ summary: Owlet is a packaging system for distributing extensions and themes for Alula
134
+ Engine
135
+ test_files:
136
+ - spec/fixtures/first.p12
137
+ - spec/fixtures/fixture.alulaextension/init.rb
138
+ - spec/fixtures/fixture.alulatheme/init.rb
139
+ - spec/fixtures/fixture.alulatheme/javascripts/application.js.coffeescript
140
+ - spec/fixtures/fixture.alulatheme/stylesheets/application.css.scss
141
+ - spec/fixtures/pleco.p12
142
+ - spec/owlet/cli_spec.rb
143
+ - spec/owlet/package_spec.rb
144
+ - spec/owlet/signer_spec.rb
145
+ - spec/spec_helper.rb