tem_ruby 0.10.0 → 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,264 +0,0 @@
1
- require 'openssl'
2
- require 'digest'
3
- require 'yaml'
4
-
5
- module Tem::CryptoAbi
6
- include Tem::Abi
7
-
8
- # The methods that will be mixed into the TEM module
9
- module MixedMethods
10
- # Reads a TEM-encoded big number.
11
- def read_tem_bignum(buffer, offset, length)
12
- return buffer[offset...(offset+length)].inject(0) do |num, digit|
13
- num = (num << 8) | digit
14
- end
15
- end
16
-
17
- # Returns the TEM encoding for a big number.
18
- def to_tem_bignum(n)
19
- if n.kind_of? OpenSSL::BN
20
- len = n.num_bytes
21
- bytes = (0...len).map do |i|
22
- bit_i = (len - i) * 8
23
- v = 0
24
- 1.upto(8) do
25
- bit_i -= 1
26
- v = (v << 1) | (n.bit_set?(bit_i) ? 1 : 0)
27
- end
28
- v
29
- end
30
- return bytes
31
- else
32
- q = 0
33
- until n == 0 do
34
- q << (n & 0xFF)
35
- n >>= 8
36
- end
37
- return q.reverse
38
- end
39
- end
40
-
41
- def load_tem_key_material(key, syms, buffer, offset)
42
- lengths = (0...syms.length).map do |i|
43
- read_tem_short buffer, offset + i * 2
44
- end
45
- offsets = [offset + syms.length * 2]
46
- syms.each_index { |i| offsets[i + 1] = offsets[i] + lengths[i] }
47
- syms.each_index do |i|
48
- key.send((syms[i].to_s + '=').to_sym,
49
- read_tem_bignum(buffer, offsets[i], lengths[i]))
50
- end
51
- end
52
-
53
- # The length of a TEM symmetric key.
54
- def tem_symmetric_key_length
55
- 16 # 128 bits
56
- end
57
-
58
- def read_tem_key(buffer, offset)
59
- key_type = read_tem_ubyte buffer, offset
60
- case key_type
61
- when 0x99
62
- key = buffer[offset, tem_symmetric_key_length]
63
- when 0xAA, 0x55
64
- key = OpenSSL::PKey::RSA.new
65
- syms = (key_type == 0xAA) ? [:e, :n] : [:p, :q, :dmp1, :dmq1, :iqmp]
66
- load_tem_key_material key, syms, buffer, offset + 1
67
- if key_type == 0x55
68
- # a bit of math to rebuild the public key
69
- key.n = key.p * key.q
70
- p1, q1 = key.p - 1, key.q - 1
71
- p1q1 = p1 * q1
72
- # HACK: I haven't figured out how to restore d from dmp1 and dmq1, so
73
- # I'm betting on the fact that e must be a small prime
74
- emp1 = key.dmp1.mod_inverse(p1)
75
- emq1 = key.dmq1.mod_inverse(q1)
76
- key.e = (emp1 < emq1) ? emp1 : emq1
77
- key.d = key.e.mod_inverse(p1q1)
78
- end
79
- else
80
- raise "Invalid key type #{'%02x' % key_type}"
81
- end
82
- return new_key_from_ssl(key, (key_type == 0xAA))
83
- end
84
-
85
- def to_tem_key(ssl_key, type)
86
- if [:private, :public].include? type
87
- # asymmetric key
88
- syms = (type == :public) ? [:e, :n] : [:p, :q, :dmp1, :dmq1, :iqmp]
89
- numbers = syms.map { |s| to_tem_bignum ssl_key.send(s) }
90
- return [(type == :public) ? 0xAA : 0x55, numbers.map { |n| to_tem_ushort(n.length) }, numbers].flatten
91
- else
92
- # symmetric key
93
- end
94
- end
95
-
96
- # Creates a new TEM key wrapper from a SSL key
97
- def new_key_from_ssl(ssl_key, is_public)
98
- if ssl_key.kind_of? OpenSSL::PKey::RSA
99
- AsymmetricKey.new ssl_key, is_public, :pkcs1
100
- else
101
- SymmetricKey.new ssl_key
102
- end
103
- end
104
-
105
- # Compute a cryptographic hash in the same way that the TEM does.
106
- def hash_for_tem(data)
107
- data = data.pack 'C*' unless data.kind_of? String
108
- Digest::SHA1.digest(data).unpack 'C*'
109
- end
110
- end
111
-
112
- self.extend MixedMethods
113
- include MixedMethods
114
- def self.included(klass)
115
- klass.extend MixedMethods
116
- end
117
-
118
- def hash_for_tem(data)
119
- Tem::CryptoAbi.hash_for_tem data
120
- end
121
-
122
- def self.load_ssl(ssl_key)
123
- return { :pubkey => AsymmetricKey.new(ssl_key, true, :pkcs1),
124
- :privkey => AsymmetricKey.new(ssl_key, false, :pkcs1) }
125
- end
126
-
127
- def self.generate_ssl_kp
128
- return Tem::CryptoAbi::AsymmetricKey.generate_ssl_kp
129
- end
130
-
131
- def self.generate_ssl_sk
132
- return Tem::CryptoAbi::SymmetricKey.generate_ssl_sk
133
- end
134
-
135
- # Wraps a TEM asymmetric key.
136
- class AsymmetricKey
137
- attr_reader :ssl_key
138
-
139
- def self.new_from_array(array)
140
- AsymmetricKey.new(OpenSSL::PKey::RSA.new(array[0]), *array[1..-1])
141
- end
142
-
143
- def self.new_from_yaml_str(yaml_str)
144
- array = YAML.load yaml_str
145
- new_from_array array
146
- end
147
-
148
- def to_array
149
- [@ssl_key.to_pem, @is_public, @padding_type]
150
- end
151
-
152
- def to_yaml_str
153
- self.to_array.to_yaml.to_s
154
- end
155
-
156
- # Generate an asymmetric OpenSSL key pair
157
- def self.generate_ssl_kp
158
- return OpenSSL::PKey::RSA.generate(2048, 65537)
159
- end
160
-
161
- def initialize(ssl_key, is_public, padding_type)
162
- @ssl_key = ssl_key
163
- @is_public = is_public ? true : false
164
- @padding_type = padding_type
165
-
166
- case padding_type
167
- when :oaep
168
- @padding_id = OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING
169
- @padding_bytes = 42
170
- when :pkcs1
171
- @padding_id = OpenSSL::PKey::RSA::PKCS1_PADDING
172
- @padding_bytes = 11
173
- else
174
- raise "Unknown padding type #{padding_type}\n"
175
- end
176
-
177
- @size = 0
178
- n = is_public ? @ssl_key.n : (@ssl_key.p * @ssl_key.q)
179
- while n != 0 do
180
- @size += 1
181
- n >>= 8
182
- end
183
- end
184
-
185
- def to_tem_key
186
- Tem::CryptoAbi.to_tem_key @ssl_key, (@is_public ? :public : :private)
187
- end
188
-
189
- def chug_data(data, in_size, &chug_block)
190
- output = data.class.new
191
- i = 0
192
- while i < data.length do
193
- block_size = (data.length - i < in_size) ? data.length - i : in_size
194
- if data.kind_of? String
195
- block = data[i...(i+block_size)]
196
- else
197
- block = data[i...(i+block_size)].pack('C*')
198
- end
199
- o_block = yield block
200
- if data.kind_of? String
201
- output += o_block
202
- else
203
- output += o_block.unpack('C*')
204
- end
205
- i += block_size
206
- end
207
- return output
208
- end
209
-
210
- def encrypt_decrypt(data, in_size, op)
211
- chug_data(data, in_size) { |block| @ssl_key.send op, block, @padding_id }
212
- end
213
-
214
- def encrypt(data)
215
- encrypt_decrypt data, @size - @padding_bytes,
216
- @is_public ? :public_encrypt : :private_encrypt
217
- end
218
-
219
- def decrypt(data)
220
- encrypt_decrypt data, @size,
221
- @is_public ? :public_decrypt : :private_decrypt
222
- end
223
-
224
- def sign(data)
225
- data = data.pack 'C*' if data.respond_to? :pack
226
- # PKCS1-padding is forced in by openssl... sigh!
227
- out_data = @ssl_key.sign OpenSSL::Digest::SHA1.new, data
228
- data.respond_to?(:pack) ? out_data : out_data.unpack('C*')
229
- end
230
-
231
- def verify(data, signature)
232
- data = data.pack 'C*' if data.respond_to? :pack
233
- signature = signature.pack 'C*' if signature.respond_to? :pack
234
- # PKCS1-padding is forced in by openssl... sigh!
235
- @ssl_key.verify OpenSSL::Digest::SHA1.new, signature, data
236
- end
237
-
238
- def is_public?
239
- @is_public
240
- end
241
- end
242
-
243
- # Wraps a TEM symmetric key
244
- def SymmetricKey
245
- def initialize(ssl_key)
246
- @key = ssl_key
247
- @cipher = OpenSSL::Cipher::Cipher.new 'aes-128-ecb'
248
- @cipher.key = @key
249
- @cipher.iv = "\0" * 16
250
- end
251
-
252
- def encrypt(data)
253
- end
254
-
255
- def decrypt(data)
256
- end
257
-
258
- def sign(data)
259
- end
260
-
261
- def verify(data)
262
- end
263
- end
264
- end