Forks

Revisions

gist: 142422 Download_button fork
public
Public Clone URL: git://gist.github.com/142422.git
Embed All Files: show embed
announce #
1
2
3
4
#
# Now this script is obsolete.
# Please use http://github.com/Constellation/crxmake instead.
#
buildex.rb #
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
#!/usr/bin/ruby
#
# Now this script is obsolete.
# Please use http://github.com/Constellation/crxmake instead.
#
# vim: fileencoding=utf-8
require 'rubygems'
require 'zipruby'
require 'openssl'
require 'digest/sha1'
require 'optparse'
require 'fileutils'
require 'find'
 
class ExCreator < Object
  @@magic = [?C, ?r, ?2, ?4].pack('C*')
  @@version = [2].pack('L')
 
  # CERT_PUBLIC_KEY_INFO struct
  @@key_algo = %w(30 81 9F 30 0D 06 09 2A 86 48 86 F7 0D 01 01 01 05 00 03 81 8D 00 30 81 89 02 81 81).map{|s| s.hex}.pack('C*')
  @@key_foot = %w(02 03 01 00 01).map{|s| s.hex}.pack('C*')
  @@key_size = 1024
  def initialize o
    check_valid_option(o)
    if @pkey
      read_key
    else
      generate_key
    end
    create_zip
    sign_zip
    write_crx
  rescue => e
    puts e.message
  ensure
    final
  end
 
  def check_valid_option o
    @exdir, @pkey, @pkey_o, @crx = o[:ex_dir], o[:pkey], o[:pkey_output], o[:crx_output]
    @exdir = File.expand_path(@exdir) if @exdir
    raise "extension dir not exist" if !@exdir || !File.exist?(@exdir) || !File.directory?(@exdir)
    @pkey = File.expand_path(@pkey) if @pkey
    raise "private key not exist" if @pkey && (!File.exist?(@pkey) || !File.file?(@pkey))
    if @pkey_o
      @pkey_o = File.expand_path(@pkey_o)
      raise "private key output path is directory" if File.directory?(@pkey_o)
    else
      count = 0
      loop do
        if count.zero?
          @pkey_o = File.expand_path("./#{File.basename(@exdir)}.pem")
        else
          @pkey_o = File.expand_path("./#{File.basename(@exdir)}-#{count+=1}.pem")
        end
        break unless File.directory?(@pkey_o)
      end
    end
    if @crx
      @crx = File.expand_path(@crx)
      raise "crx path is directory" if File.directory?(@crx)
    else
      count = 0
      loop do
        if count.zero?
          @crx = File.expand_path("./#{File.basename(@exdir)}.crx")
        else
          @crx = File.expand_path("./#{File.basename(@exdir)}-#{count+=1}.crx")
        end
        break unless File.directory?(@crx)
      end
    end
    @crx_dir = File.dirname(@crx)
    @zip = File.join(@crx_dir, 'extension.zip')
  end
 
  def read_key
    File.open(@pkey, 'rb') do |io|
      @key = OpenSSL::PKey::RSA.new(io)
    end
  end
 
  def generate_key
    @key = OpenSSL::PKey::RSA.generate(@@key_size)
    # save key
    File.open(@pkey_o, 'wb') do |file|
      file << @key.export()
    end
  end
 
  def create_zip
    Zip::Archive.open(@zip, Zip::CREATE | Zip::TRUNC) do |zip|
      Find.find(@exdir) do |path|
        unless path == @exdir
          if File.directory?(path)
            if File.basename(path)[0] == ?.
              Find.prune
            else
              zip.add_dir(get_relative(@exdir, path))
            end
          else
            zip.add_file(get_relative(@exdir, path), path)
          end
        end
      end
    end
  end
 
  def get_relative base, target
    if base == target
      return '.'
    end
    if base[base.size - 1] != ?/
      base += '/'
    end
    target.sub(Regexp.escape(base), '')
  end
 
  def sign_zip
    plain = nil
    File.open(@zip, 'rb') do |file|
      plain = file.read
    end
    @sig = @key.sign(OpenSSL::Digest::SHA1.new, plain)
  end
 
  def write_crx
    key = @@key_algo + key_data + @@key_foot
    File.open(@crx, 'wb') do |file|
      file << @@magic
      file << @@version
      file << to_sizet(key.size)
      file << to_sizet(@sig.size)
      file << key
      file << @sig
      File.open(@zip, 'rb') do |zip|
        file << zip.read
      end
    end
  end
 
  def key_data
    pubkey = @key.public_key
    memo = pubkey.to_text.split(/\r|\n|\n\r/).inject({:flag => false, :data => []}){|memo, line|
      if memo[:flag]
        if line =~ /^\s+/
          memo[:data].push(*line.strip.split(':'))
        else
          memo[:flag] = false
        end
      elsif /^Modulus/ =~ line
        memo[:flag] = true
      end
      memo
    }
    data = memo[:data].map{|s| s.hex}.pack('C*')
    return data
  end
 
  def to_sizet num
    return [num].pack('L')
  end
 
  def final
    FileUtils.rm_rf(@zip) if @zip && File.exist?(@zip)
  end
end
 
# main
data = {}
OptionParser.new('Packaging Chromium Extension') do |opt|
  opt.version = '2'
  opt.on('--pack-extension DIR') do |val|
    data[:ex_dir] = val
  end
  opt.on('--pack-extension-key KEY') do |key|
    data[:pkey] = key
  end
  opt.on('--key-output OKEY') do |okey|
    data[:pkey_output] = okey
  end
  opt.on('--extension-output CRX') do |crx|
    data[:crx_output] = crx
  end
  opt.parse!(ARGV)
end
ExCreator.new(data)