aboutsummaryrefslogtreecommitdiffstats
path: root/lib/openssl.rb
blob: 8baf452acb05b906edc5db9bcc6161b946c4af1a (plain)
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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
#!/usr/bin/env ruby

require 'openssl.so'
require 'openssl/buffering'
require 'thread'

module OpenSSL
  module PKey
if defined? DSA
    class DSA
      def DSA::new(arg, pass=nil)
        if arg.kind_of? Fixnum
	  DSA::generate(arg) {|p,n|
	    if block_given? then yield [p,n] end
	  }
	else
	  DSA::new_from_pem(arg, pass)
	end
      end # DSA::new
      #
      # DSA::new_from_pem(PEM string, pass) is built-in
      # DSA::new_from_fixnum(size) is an alias to DSA::generate(size)
      # DSA::generate(size) is built-in; yields p,n
      # 
      def sign(digest, data)
	unless self.private?
	  raise OpenSSL::PKey::DSAError, "Cannot sign with public key!"
	end
	unless digest.kind_of? OpenSSL::Digest::ANY
	  raise TypeError, "digest alg needed! (got #{digest.class})"
	end
	self.sign_digest digest.update(data.to_s).digest
      end # sign
      
      def verify(digest, signature, data)
	unless digest.kind_of? OpenSSL::Digest::ANY
	  raise TypeError, "digest alg needed! (got #{digest.class})"
	end
	unless signature.class == String
	  raise TypeError, "Signature as String expected (got #{sign.class})"
	end
	self.verify_digest(digest.update(data.to_s).digest, signature)
      end # verify
    end # DSA
end #defined? DSA
if defined? RSA
    class RSA
      def RSA::new(arg, pass=nil)
        if arg.kind_of? Fixnum
	  RSA::generate(arg) {|p,n|
	    if block_given? then yield [p,n] end
	  }
	else
	  RSA::new_from_pem(arg, pass)
	end
      end # RSA::new
      #
      # RSA::new_from_pem(PEM string, pass) is built-in
      # RSA::new_from_fixnum(size) is an alias to RSA::generate(size)
      # RSA::generate(size) is built-in; yields p,n
      # 
      def sign(digest, data)
	unless self.private?
	  raise OpenSSL::PKey::RSAError, "Cannot sign with public key!"
	end
	unless digest.kind_of? OpenSSL::Digest::ANY
	  raise TypeError, "digest alg needed! (got #{digest.class})"
	end
	self.private_encrypt digest.update(data.to_s).digest
      end # sign
      
      def verify(digest, signature, data)
	unless digest.kind_of? OpenSSL::Digest::ANY
	  raise TypeError, "digest alg needed! (got #{digest.class})"
	end
	unless signature.class == String
	  raise TypeError, "Signature as String expected (got #{sign.class})"
	end
	md_s = self.public_decrypt signature
	md_d = digest.update(data.to_s).digest
	md_s == md_d
      end # verify
    end # RSA
end # defined? RSA
if defined? DH
    class DH
      def DH::new(arg, gen = 2)
        if arg.kind_of? Fixnum
	  DH::generate(arg, gen) {|p,n|
	    if block_given? then yield [p,n] end
	  }
	else
	  DH::new_from_pem(arg)
	end
      end # DH::new
      #
      # DH::new_from_pem(PEM string, pass) is built-in
      # DH::new_from_fixnum(size, gen) is an alias to DH::generate(size, gen)
      # DH::generate(size, gen) is built-in; yields p,n
      #
    end # DH
end # defined? DH
  end # PKey

  module SSL
    class SSLSocket
      include Buffering
      CallbackMutex = Mutex.new
      
      def connect
        CallbackMutex.synchronize{ __connect }
      end
      
      def accept
        CallbackMutex.synchronize{ __accept }
      end
    end # SSLSocket
  end # SSL
  
  module X509
    class Name
      def Name::new(arg)
        type = arg.class
	while type
	  method = "new_from_#{type.name.downcase}".intern
	  return Name::send(method, arg) if Name::respond_to? method
	  type = type.superclass
	end
	raise TypeError, "Don't how to make new #{self} from #{arg.class}"
        ###Name::send("new_from_#{arg.class.name.downcase}", arg)
      end
      #
      # Name::new_from_hash(hash) is built-in method
      # 
      def Name::new_from_string(str) # we're expecting string like "/A=B/C=D/E=F"
        hash = Hash::new
	key = val = nil # speed optim.
	ary = str.split("/")
	ary.shift # first item is "" - so skip it
	ary.each {|item|
	  key, val = item.split("=")
	  hash[key] = val
	}
	Name::new_from_hash(hash)
	###ary.collect! {|item| item.split("=") }
	###Name::new_from_array(ary)
      end
      
      def Name::new_from_array(ary) # [["A","B"],["C","D"],["E","F"]]
	hash = Hash::new
	ary.each {|key, val|
	  hash[key] = val
	}
	Name::new_from_hash(hash)
      end
      #
      # to_h is built-in method
      # 
      def to_str # "/A=B/C=D/E=F"
        hash = self.to_h
	str = ""
	hash.keys.each do |key|
	  str += "/" + key + "=" + hash[key]
	end
	str
      end
      
      def to_a # [["A","B"],["C","D"],["E","F"]]
        self.to_h.to_a
      end
    end # Name
    
    class ExtensionFactory
      def create_extension(*arg)
        if arg.size == 1 then arg = arg[0] end
	type = arg.class
	while type
	  method = "create_ext_from_#{type.name.downcase}".intern
	  return send(method, arg) if respond_to? method
	  type = type.superclass
	end
	raise TypeError, "Don't how to create ext from #{arg.class}"
	###send("create_ext_from_#{arg.class.name.downcase}", arg)
      end
      #
      # create_ext_from_array is built-in
      #
      def create_ext_from_string(str) # "oid = critical, value"
	unless str =~ /\s*=\s*/
	  raise ArgumentError, "string in format \"oid = value\" expected"
	end
        ary = []
	ary << $`.sub(/^\s*/,"") # delete whitespaces from the beginning
	rest = $'.sub(/\s*$/,"") # delete them from the end
	if rest =~ /^critical,\s*/ # handle 'critical' option
	  ary << $'
	  ary << true
	else
	  ary << rest
	end
	create_ext_from_array(ary)
      end
      
      def create_ext_from_hash(hash) # {"oid"=>sn|ln, "value"=>value, "critical"=>true|false}
	unless (hash.has_key? "oid" and hash.has_key? "value")
	  raise ArgumentError, "hash in format {\"oid\"=>..., \"value\"=>...} expected"
	end
        ary = []
	ary << hash["oid"]
	ary << hash["value"]
	ary << hash["critical"] if hash.has_key? "critical"
        create_ext_from_array(ary)
      end
    end # ExtensionFactory
    
    class Extension
      # note: Extension.new is UNDEFed! - use ExtensionFactory.create_extension
      #
      # to_a is built-in
      # 
      def to_str # "oid = critical, value"
        ary = self.to_a
	str = ary[0] + " = "
	str += "critical, " if ary[2] == true
	str += ary[1]
      end
      
      def to_h # {"oid"=>sn|ln, "value"=>value, "critical"=>true|false}
        ary = self.to_a
	{"oid"=>ary[0],"value"=>ary[1],"critical"=>ary[2]}
      end

      def oid
        self.to_a[0]
      end

      def value
        self.to_a[1]
      end
      
      def critical?
        self.to_a[2]
      end
    end # Extension
    
    class Attribute
      def Attribute::new(arg)
        type = arg.class
	while type
	  method = "new_from_#{type.name.downcase}".intern
	  return Attribute::send(method, arg) if Attribute::respond_to? method
	  type = type.superclass
	end
	raise "Don't how to make new #{self} from #{arg.class}"
        ###Attribute::send("new_from_#{arg.class.name.downcase}", arg)
      end
      #
      # Attribute::new_from_array(ary) is built-in method
      #
      def Attribute::new_from_string(str) # "oid = value"
	unless str =~ /\s*=\s*/
	  raise ArgumentError, "string in format \"oid = value\" expected"
	end
        ary = []
	ary << $`.sub(/^\s*/,"") # delete whitespaces from the beginning
	ary << $'.sub(/\s*$/,"") # delete them from the end
	Attribute::new_from_array(ary)
      end

      def Attribute::new_from_hash(hash) # {"oid"=>"...", "value"=>"..."}
	unless (hash.has_key? "oid" and hash.has_key? "value")
	  raise ArgumentError, "hash in format {\"oid\"=>..., \"value\"=>...} expected"
	end
        ary = []
	ary << hash["oid"]
	ary << hash["value"]
	Attribute::new_from_array(ary)
      end
    end # Attribute
  end # X509

  class BN
    def initialize(arg=nil, type="dec")
    	return if arg.nil?
	t = arg.class
	while t
	  method = "from_#{t.name.downcase.split("::").last}".intern
	  return send(method, arg, type) if respond_to?(method, true)
	  t = t.superclass
	end
	raise "Don't how to init #{self.class.name} from #{arg.class}"
    end

    def from_bn(arg, dummy=nil)
      copy(arg)
    end

    def from_integer(arg, type="dec")
      from_string(arg.to_s, type)
    end

    def from_string(arg, type="dec")
      send("from_s_#{type.downcase}", arg)
    end

    private :from_bn, :from_integer, :from_string
    
    def to_s(type="dec")
      send("to_s_#{type.downcase}")
    end

    def to_i
      self.to_s.to_i
    end
  end # BN
end # OpenSSL

class Integer
  def to_bn
    OpenSSL::BN::new(self)
  end
end # Integer