pkcs11 0.1.0-x86-mswin32 → 0.2.0-x86-mswin32
Sign up to get free protection for your applications and to get access to all the features.
- data/.yardopts +2 -0
- data/History.txt +23 -0
- data/Manifest.txt +6 -0
- data/README.rdoc +28 -15
- data/Rakefile +51 -2
- data/ext/extconf.rb +1 -0
- data/ext/generate_structs.rb +157 -0
- data/ext/generate_thread_funcs.rb +72 -0
- data/ext/pk11.c +482 -252
- data/ext/pk11.h +4 -1
- data/ext/pk11_const.c +25 -476
- data/ext/pk11_const_def.inc +452 -0
- data/ext/pk11_struct_def.inc +304 -0
- data/ext/pk11_struct_impl.inc +304 -0
- data/ext/pk11_thread_funcs.c +411 -0
- data/ext/pk11_thread_funcs.h +482 -0
- data/lib/1.8/pkcs11_ext.so +0 -0
- data/lib/1.9/pkcs11_ext.so +0 -0
- data/lib/pkcs11.rb +5 -8
- data/lib/pkcs11/extensions.rb +17 -109
- data/lib/pkcs11/helper.rb +151 -0
- data/lib/pkcs11/library.rb +44 -13
- data/lib/pkcs11/object.rb +73 -17
- data/lib/pkcs11/session.rb +318 -121
- data/lib/pkcs11/slot.rb +30 -9
- data/test/helper.rb +13 -6
- data/test/test_pkcs11.rb +13 -2
- data/test/test_pkcs11_crypt.rb +38 -3
- data/test/test_pkcs11_object.rb +18 -4
- data/test/test_pkcs11_session.rb +28 -2
- data/test/test_pkcs11_slot.rb +9 -6
- data/test/test_pkcs11_structs.rb +134 -0
- data/test/test_pkcs11_thread.rb +45 -0
- metadata +40 -7
data/lib/pkcs11/session.rb
CHANGED
@@ -1,93 +1,69 @@
|
|
1
|
+
require 'pkcs11/helper'
|
2
|
+
|
1
3
|
module PKCS11
|
2
4
|
# Cryptoki requires that an application open one or more sessions with a token to gain
|
3
|
-
# access to the token
|
5
|
+
# access to the token's objects and functions. A session provides a logical connection
|
4
6
|
# between the application and the token. A session can be a read/write (R/W) session or a
|
5
7
|
# read-only (R/O) session (default).
|
6
8
|
class Session
|
7
|
-
|
8
|
-
def hash_to_attributes(template) # :nodoc:
|
9
|
-
case template
|
10
|
-
when Array
|
11
|
-
template.map{|v| PKCS11::CK_ATTRIBUTE.new(string_to_handle('CKA_', v), nil) }
|
12
|
-
when Hash
|
13
|
-
template.map{|k,v| PKCS11::CK_ATTRIBUTE.new(string_to_handle('CKA_', k), v) }
|
14
|
-
when String, Symbol
|
15
|
-
[PKCS11::CK_ATTRIBUTE.new(string_to_handle('CKA_', template), nil)]
|
16
|
-
when Integer
|
17
|
-
[PKCS11::CK_ATTRIBUTE.new(template, nil)]
|
18
|
-
else
|
19
|
-
template
|
20
|
-
end
|
21
|
-
end
|
9
|
+
include Helper
|
22
10
|
|
23
|
-
|
24
|
-
case attribute
|
25
|
-
when String, Symbol
|
26
|
-
PKCS11.const_get("#{prefix}#{attribute}")
|
27
|
-
else
|
28
|
-
attribute
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def hash_to_mechanism(hash) # :nodoc:
|
33
|
-
case hash
|
34
|
-
when String, Symbol
|
35
|
-
PKCS11::CK_MECHANISM.new(string_to_handle('CKM_', hash))
|
36
|
-
when Hash
|
37
|
-
raise "only one mechanism allowed" unless hash.length==1
|
38
|
-
PKCS11::CK_MECHANISM.new(string_to_handle('CKM_', hash.keys.first), hash.values.first)
|
39
|
-
else
|
40
|
-
hash.to_int
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
11
|
+
# @private
|
45
12
|
def initialize(pkcs11, session) # :nodoc:
|
46
13
|
@pk, @sess = pkcs11, session
|
47
14
|
end
|
48
15
|
|
49
16
|
# The session handle.
|
17
|
+
# @return [Integer]
|
50
18
|
def to_int
|
51
19
|
@sess
|
52
20
|
end
|
53
21
|
alias to_i to_int
|
54
22
|
|
23
|
+
# @private
|
55
24
|
def inspect # :nodoc:
|
56
25
|
"#<#{self.class} #{@sess.inspect}>"
|
57
26
|
end
|
58
27
|
|
59
28
|
# Logs a user into a token.
|
60
|
-
# user_type
|
61
|
-
# pin
|
29
|
+
# @param [Integer, Symbol] user_type is the user type CKU_*;
|
30
|
+
# @param [String] pin is the user's PIN.
|
31
|
+
# @return [PKCS11::Session]
|
62
32
|
#
|
63
33
|
# When the user type is either CKU_SO or CKU_USER, if the call succeeds, each of the
|
64
34
|
# application's sessions will enter either the "R/W SO Functions" state, the "R/W User
|
65
35
|
# Functions" state, or the "R/O User Functions" state. If the user type is
|
66
36
|
# CKU_CONTEXT_SPECIFIC , the behavior of C_Login depends on the context in which
|
67
|
-
# it is called. Improper use of this user type will
|
37
|
+
# it is called. Improper use of this user type will raise
|
68
38
|
# CKR_OPERATION_NOT_INITIALIZED.
|
69
39
|
def C_Login(user_type, pin)
|
70
|
-
@pk.C_Login(@sess,
|
40
|
+
@pk.C_Login(@sess, string_to_handle('CKU_', user_type), pin)
|
41
|
+
self
|
71
42
|
end
|
72
43
|
alias login C_Login
|
73
44
|
|
74
45
|
# Logs a user out from a token.
|
75
46
|
#
|
76
|
-
# Depending on the current user type, if the call succeeds, each of the application
|
77
|
-
# sessions will enter either the
|
47
|
+
# Depending on the current user type, if the call succeeds, each of the application's
|
48
|
+
# sessions will enter either the "R/W Public Session" state or the "R/O Public Session"
|
78
49
|
# state.
|
50
|
+
# @return [PKCS11::Session]
|
79
51
|
def C_Logout()
|
80
52
|
@pk.C_Logout(@sess)
|
53
|
+
self
|
81
54
|
end
|
82
55
|
alias logout C_Logout
|
83
56
|
|
84
57
|
# Closes the session between an application and a token.
|
58
|
+
# @return [PKCS11::Session]
|
85
59
|
def C_CloseSession()
|
86
60
|
@pk.C_CloseSession(@sess)
|
61
|
+
self
|
87
62
|
end
|
88
63
|
alias close C_CloseSession
|
89
64
|
|
90
|
-
# Obtains information about a session.
|
65
|
+
# Obtains information about a session.
|
66
|
+
# @return [CK_SESSION_INFO]
|
91
67
|
def C_GetSessionInfo()
|
92
68
|
@pk.C_GetSessionInfo(@sess)
|
93
69
|
end
|
@@ -96,35 +72,43 @@ module PKCS11
|
|
96
72
|
# Initializes a search for token and session objects that match a
|
97
73
|
# template.
|
98
74
|
#
|
99
|
-
#
|
75
|
+
# See {Session#find_objects} for convenience.
|
76
|
+
# @param [Hash] find_template points to a search template that
|
100
77
|
# specifies the attribute values to match
|
101
|
-
#
|
102
|
-
#
|
103
|
-
|
78
|
+
# The matching criterion is an exact byte-for-byte match with all attributes in the
|
79
|
+
# template. Use empty Hash to find all objects.
|
80
|
+
# @return [PKCS11::Session]
|
104
81
|
def C_FindObjectsInit(find_template={})
|
105
|
-
@pk.C_FindObjectsInit(@sess,
|
82
|
+
@pk.C_FindObjectsInit(@sess, to_attributes(find_template))
|
83
|
+
self
|
106
84
|
end
|
107
85
|
|
108
86
|
# Continues a search for token and session objects that match a template,
|
109
87
|
# obtaining additional object handles.
|
110
88
|
#
|
111
|
-
#
|
89
|
+
# See {Session#find_objects} for convenience
|
90
|
+
# @return [Array<PKCS11::Object>] Returns an array of Object instances.
|
112
91
|
def C_FindObjects(max_count)
|
113
92
|
objs = @pk.C_FindObjects(@sess, max_count)
|
114
93
|
objs.map{|obj| Object.new @pk, @sess, obj }
|
115
94
|
end
|
116
95
|
|
117
96
|
# Terminates a search for token and session objects.
|
97
|
+
#
|
98
|
+
# See {Session#find_objects} for convenience
|
99
|
+
# @return [PKCS11::Session]
|
118
100
|
def C_FindObjectsFinal
|
119
101
|
@pk.C_FindObjectsFinal(@sess)
|
102
|
+
self
|
120
103
|
end
|
121
104
|
|
122
|
-
# Convenience method for the C_FindObjectsInit, C_FindObjects, C_FindObjectsFinal cycle.
|
105
|
+
# Convenience method for the {Session#C_FindObjectsInit}, {Session#C_FindObjects}, {Session#C_FindObjectsFinal} cycle.
|
123
106
|
#
|
124
107
|
# * If called with block, it iterates over all found objects.
|
125
108
|
# * If called without block, it returns with an array of all found Object instances.
|
109
|
+
# @return [Array<PKCS11::Object>]
|
126
110
|
#
|
127
|
-
#
|
111
|
+
# @example prints subject of all certificates stored in the token:
|
128
112
|
# session.find_objects(:CLASS => PKCS11::CKO_CERTIFICATE) do |obj|
|
129
113
|
# p OpenSSL::X509::Name.new(obj[:SUBJECT])
|
130
114
|
# end
|
@@ -148,39 +132,61 @@ module PKCS11
|
|
148
132
|
end
|
149
133
|
|
150
134
|
|
151
|
-
# Creates a new Object based on given template.
|
152
|
-
#
|
135
|
+
# Creates a new Object based on given template.
|
136
|
+
#
|
137
|
+
# If {Session#C_CreateObject} is used to create a key object, the key object will have its
|
153
138
|
# CKA_LOCAL attribute set to false. If that key object is a secret or private key
|
154
139
|
# then the new key will have the CKA_ALWAYS_SENSITIVE attribute set to
|
155
140
|
# false, and the CKA_NEVER_EXTRACTABLE attribute set to false.
|
156
141
|
#
|
157
142
|
# Only session objects can be created during a read-only session. Only public objects can
|
158
143
|
# be created unless the normal user is logged in.
|
144
|
+
#
|
145
|
+
# @param [Hash] template Attributes of the object to create.
|
146
|
+
# @return [PKCS11::Object] the newly created object
|
147
|
+
# @example Creating a 112 bit DES key from plaintext
|
148
|
+
# secret_key = session.create_object(
|
149
|
+
# :CLASS=>PKCS11::CKO_SECRET_KEY, :KEY_TYPE=>PKCS11::CKK_DES2,
|
150
|
+
# :ENCRYPT=>true, :WRAP=>true, :DECRYPT=>true, :UNWRAP=>true,
|
151
|
+
# :VALUE=>'0123456789abcdef', :LABEL=>'test_secret_key')
|
159
152
|
def C_CreateObject(template={})
|
160
|
-
handle = @pk.C_CreateObject(@sess,
|
153
|
+
handle = @pk.C_CreateObject(@sess, to_attributes(template))
|
161
154
|
Object.new @pk, @sess, handle
|
162
155
|
end
|
163
156
|
alias create_object C_CreateObject
|
164
157
|
|
165
|
-
# Initializes the normal user
|
158
|
+
# Initializes the normal user's PIN. This standard
|
166
159
|
# allows PIN values to contain any valid UTF8 character, but the token may impose subset
|
167
160
|
# restrictions.
|
161
|
+
#
|
162
|
+
# @param [String] pin
|
163
|
+
# @return [PKCS11::Session]
|
168
164
|
def C_InitPIN(pin)
|
169
165
|
@pk.C_InitPIN(@sess, pin)
|
166
|
+
self
|
170
167
|
end
|
171
168
|
alias init_pin C_InitPIN
|
172
169
|
|
173
170
|
# Modifies the PIN of the user that is currently logged in, or the CKU_USER
|
174
171
|
# PIN if the session is not logged in.
|
172
|
+
#
|
173
|
+
# @param [String] old_pin
|
174
|
+
# @param [String] new_pin
|
175
|
+
# @return [PKCS11::Session]
|
175
176
|
def C_SetPIN(old_pin, new_pin)
|
176
177
|
@pk.C_SetPIN(@sess, old_pin, new_pin)
|
178
|
+
self
|
177
179
|
end
|
178
180
|
alias set_pin C_SetPIN
|
179
181
|
|
180
182
|
class Cipher
|
183
|
+
# @private
|
181
184
|
def initialize(update_block) # :nodoc:
|
182
185
|
@update_block = update_block
|
183
186
|
end
|
187
|
+
# Process a data part with the encryption operation.
|
188
|
+
# @param [String] data data to be processed
|
189
|
+
# @return [String] output data
|
184
190
|
def update(data)
|
185
191
|
@update_block.call(data)
|
186
192
|
end
|
@@ -188,11 +194,16 @@ module PKCS11
|
|
188
194
|
end
|
189
195
|
|
190
196
|
class DigestCipher < Cipher
|
197
|
+
# @private
|
191
198
|
def initialize(update_block, digest_key_block) # :nodoc:
|
192
199
|
super(update_block)
|
193
200
|
@digest_key_block = digest_key_block
|
194
201
|
end
|
195
202
|
alias digest_update update
|
203
|
+
# Continues a multiple-part message-digesting operation by digesting the
|
204
|
+
# value of a secret key.
|
205
|
+
# @param [PKCS11::Object] key key to be processed
|
206
|
+
# @return [String] output data
|
196
207
|
def digest_key(key)
|
197
208
|
@digest_key_block.call(key)
|
198
209
|
end
|
@@ -228,53 +239,81 @@ module PKCS11
|
|
228
239
|
|
229
240
|
# Initializes an encryption operation.
|
230
241
|
#
|
231
|
-
#
|
232
|
-
#
|
242
|
+
# @param [Hash, Symbol, Integer, PKCS11::CK_MECHANISM] mechanism the encryption mechanism
|
243
|
+
# @param [PKCS11::Object] key the object handle of the encryption key.
|
244
|
+
# @return [PKCS11::Session]
|
233
245
|
#
|
234
|
-
# See Session#encrypt for convenience
|
246
|
+
# See {Session#encrypt} for convenience
|
235
247
|
#
|
236
248
|
# The CKA_ENCRYPT attribute of the encryption key, which indicates whether the key
|
237
249
|
# supports encryption, must be true.
|
238
250
|
#
|
239
|
-
# After calling C_EncryptInit, the application can either call C_Encrypt to encrypt data
|
240
|
-
# in a single part; or call C_EncryptUpdate zero or more times, followed by
|
241
|
-
# C_EncryptFinal, to encrypt data in multiple parts. The encryption operation is active
|
242
|
-
# until the application uses a call to C_Encrypt or C_EncryptFinal to actually obtain the
|
251
|
+
# After calling {Session#C_EncryptInit}, the application can either call {Session#C_Encrypt} to encrypt data
|
252
|
+
# in a single part; or call {Session#C_EncryptUpdate} zero or more times, followed by
|
253
|
+
# {Session#C_EncryptFinal}, to encrypt data in multiple parts. The encryption operation is active
|
254
|
+
# until the application uses a call to {Session#C_Encrypt} or {Session#C_EncryptFinal} to actually obtain the
|
243
255
|
# final piece of ciphertext. To process additional data (in single or multiple parts), the
|
244
|
-
# application must call C_EncryptInit again.
|
256
|
+
# application must call {Session#C_EncryptInit} again.
|
245
257
|
def C_EncryptInit(mechanism, key)
|
246
|
-
@pk.C_EncryptInit(@sess,
|
258
|
+
@pk.C_EncryptInit(@sess, to_mechanism(mechanism), key)
|
259
|
+
self
|
247
260
|
end
|
248
261
|
# Encrypts single-part data.
|
262
|
+
#
|
263
|
+
# See {Session#encrypt} for convenience
|
264
|
+
# @param [Integer, nil] out_size The buffer size for output data provided to the
|
265
|
+
# library. If nil, size is determined automatically.
|
266
|
+
# @return [String]
|
249
267
|
def C_Encrypt(data, out_size=nil)
|
250
268
|
@pk.C_Encrypt(@sess, data, out_size)
|
251
269
|
end
|
252
270
|
# Continues a multiple-part encryption operation, processing another
|
253
271
|
# data part.
|
272
|
+
#
|
273
|
+
# See {Session#encrypt} for convenience
|
274
|
+
# @param [Integer, nil] out_size The buffer size for output data provided to the
|
275
|
+
# library. If nil, size is determined automatically.
|
276
|
+
# @return [String]
|
254
277
|
def C_EncryptUpdate(data, out_size=nil)
|
255
278
|
@pk.C_EncryptUpdate(@sess, data, out_size)
|
256
279
|
end
|
257
280
|
# Finishes a multiple-part encryption operation.
|
281
|
+
#
|
282
|
+
# See {Session#encrypt} for convenience
|
283
|
+
# @param [Integer, nil] out_size The buffer size for output data provided to the
|
284
|
+
# library. If nil, size is determined automatically.
|
285
|
+
# @return [String]
|
258
286
|
def C_EncryptFinal(out_size=nil)
|
259
287
|
@pk.C_EncryptFinal(@sess, out_size)
|
260
288
|
end
|
261
289
|
|
262
|
-
# Convenience method for the C_EncryptInit, C_EncryptUpdate, C_EncryptFinal call flow.
|
290
|
+
# Convenience method for the {Session#C_EncryptInit}, {Session#C_EncryptUpdate}, {Session#C_EncryptFinal} call flow.
|
263
291
|
#
|
264
|
-
# If no block is given, the single part operation C_EncryptInit, C_Encrypt is called.
|
265
|
-
# If a block is given, the multi part operation (C_EncryptInit, C_EncryptUpdate, C_EncryptFinal)
|
292
|
+
# If no block is given, the single part operation {Session#C_EncryptInit}, {Session#C_Encrypt} is called.
|
293
|
+
# If a block is given, the multi part operation ({Session#C_EncryptInit}, {Session#C_EncryptUpdate}, {Session#C_EncryptFinal})
|
266
294
|
# is used. The given block is called once with a cipher object. There can be any number of
|
267
|
-
# Cipher#update calls within the block, each giving the encryption result of this part as String.
|
295
|
+
# {Cipher#update} calls within the block, each giving the encryption result of this part as String.
|
268
296
|
#
|
269
|
-
#
|
297
|
+
# @param [Hash, Symbol, Integer, PKCS11::CK_MECHANISM] mechanism used mechanism
|
298
|
+
# @param [PKCS11::Object] key used key
|
299
|
+
# @param [String] data data to encrypt
|
300
|
+
# @yield [PKCS11::Session::Cipher] Cipher object for processing data parts
|
301
|
+
# @return [String] the final part of the encryption operation.
|
270
302
|
#
|
271
|
-
#
|
303
|
+
# @example for using single part operation
|
304
|
+
# iv = "12345678"
|
305
|
+
# cryptogram = session.encrypt( {:DES_CBC_PAD=>iv}, key, "block 1block 2" )
|
306
|
+
#
|
307
|
+
# @example for using multi part operation
|
272
308
|
# iv = "12345678"
|
273
309
|
# cryptogram = ''
|
274
310
|
# cryptogram << session.encrypt( {:DES_CBC_PAD=>iv}, key ) do |cipher|
|
275
311
|
# cryptogram << cipher.update("block 1")
|
276
312
|
# cryptogram << cipher.update("block 2")
|
277
313
|
# end
|
314
|
+
#
|
315
|
+
# @example Calculating a key check value to a secret key
|
316
|
+
# key_kcv = session.encrypt( :DES3_ECB, key, "\0"*8)
|
278
317
|
def encrypt(mechanism, key, data=nil, &block)
|
279
318
|
common_crypt(:C_EncryptInit, :C_EncryptUpdate, :C_EncryptFinal, :C_Encrypt,
|
280
319
|
mechanism, key, data, &block)
|
@@ -282,27 +321,50 @@ module PKCS11
|
|
282
321
|
|
283
322
|
# Initializes a decryption operation.
|
284
323
|
#
|
285
|
-
# See Session#decrypt for convenience.
|
324
|
+
# See {Session#decrypt} for convenience and {Session#C_EncryptInit} for description.
|
286
325
|
def C_DecryptInit(mechanism, key)
|
287
|
-
@pk.C_DecryptInit(@sess,
|
326
|
+
@pk.C_DecryptInit(@sess, to_mechanism(mechanism), key)
|
288
327
|
end
|
289
328
|
# Decrypts encrypted data in a single part.
|
329
|
+
#
|
330
|
+
# See {Session#decrypt} for convenience.
|
331
|
+
# @param [Integer, nil] out_size The buffer size for output data provided to the
|
332
|
+
# library. If nil, size is determined automatically.
|
333
|
+
# @return [String]
|
290
334
|
def C_Decrypt(data, out_size=nil)
|
291
335
|
@pk.C_Decrypt(@sess, data, out_size)
|
292
336
|
end
|
293
337
|
# Continues a multiple-part decryption operation, processing another
|
294
338
|
# encrypted data part.
|
339
|
+
#
|
340
|
+
# See {Session#decrypt} for convenience.
|
341
|
+
# @param [Integer, nil] out_size The buffer size for output data provided to the
|
342
|
+
# library. If nil, size is determined automatically.
|
343
|
+
# @return [String]
|
295
344
|
def C_DecryptUpdate(data, out_size=nil)
|
296
345
|
@pk.C_DecryptUpdate(@sess, data, out_size)
|
297
346
|
end
|
298
347
|
# Finishes a multiple-part decryption operation.
|
348
|
+
#
|
349
|
+
# See {Session#decrypt} for convenience.
|
350
|
+
# @param [Integer, nil] out_size The buffer size for output data provided to the
|
351
|
+
# library. If nil, size is determined automatically.
|
352
|
+
# @return [String]
|
299
353
|
def C_DecryptFinal(out_size=nil)
|
300
354
|
@pk.C_DecryptFinal(@sess, out_size)
|
301
355
|
end
|
302
356
|
|
303
|
-
# Convenience method for the C_DecryptInit, C_DecryptUpdate, C_DecryptFinal call flow.
|
357
|
+
# Convenience method for the {Session#C_DecryptInit}, {Session#C_DecryptUpdate}, {Session#C_DecryptFinal} call flow.
|
304
358
|
#
|
305
|
-
#
|
359
|
+
# @see Session#encrypt
|
360
|
+
#
|
361
|
+
# @param [Hash, Symbol, Integer, PKCS11::CK_MECHANISM] mechanism used mechanism
|
362
|
+
# @param [PKCS11::Object] key used key
|
363
|
+
# @param [String] data data to decrypt
|
364
|
+
# @yield [PKCS11::Session::Cipher] Cipher object for processing data parts
|
365
|
+
# @return [String] the final part of the encryption operation.
|
366
|
+
# @example Decrypt data previously encrypted with a RSA pulic key
|
367
|
+
# plaintext2 = session.decrypt( :RSA_PKCS, rsa_priv_key, cryptogram)
|
306
368
|
def decrypt(mechanism, key, data=nil, &block)
|
307
369
|
common_crypt(:C_DecryptInit, :C_DecryptUpdate, :C_DecryptFinal, :C_Decrypt,
|
308
370
|
mechanism, key, data, &block)
|
@@ -310,42 +372,67 @@ module PKCS11
|
|
310
372
|
|
311
373
|
# Initializes a message-digesting operation.
|
312
374
|
#
|
313
|
-
# See Session#digest for convenience.
|
375
|
+
# See {Session#digest} for convenience.
|
376
|
+
# @return [PKCS11::Session]
|
314
377
|
def C_DigestInit(mechanism)
|
315
|
-
@pk.C_DigestInit(@sess,
|
378
|
+
@pk.C_DigestInit(@sess, to_mechanism(mechanism))
|
379
|
+
self
|
316
380
|
end
|
317
381
|
# Digests data in a single part.
|
382
|
+
#
|
383
|
+
# See {Session#digest} for convenience.
|
384
|
+
# @param [Integer, nil] out_size The buffer size for output data provided to the
|
385
|
+
# library. If nil, size is determined automatically.
|
386
|
+
# @return [String]
|
318
387
|
def C_Digest(data, out_size=nil)
|
319
388
|
@pk.C_Digest(@sess, data, out_size)
|
320
389
|
end
|
321
390
|
# Continues a multiple-part message-digesting operation, processing
|
322
391
|
# another data part.
|
392
|
+
#
|
393
|
+
# See {Session#digest} for convenience.
|
394
|
+
# @return [PKCS11::Session]
|
323
395
|
def C_DigestUpdate(data)
|
324
396
|
@pk.C_DigestUpdate(@sess, data)
|
397
|
+
self
|
325
398
|
end
|
326
399
|
# Continues a multiple-part message-digesting operation by digesting the
|
327
400
|
# value of a secret key.
|
328
401
|
#
|
329
|
-
#
|
330
|
-
#
|
402
|
+
# See {Session#digest} for convenience.
|
403
|
+
#
|
404
|
+
# The message-digesting operation must have been initialized with {Session#C_DigestInit}. Calls to
|
405
|
+
# this function and {Session#C_DigestUpdate} may be interspersed any number of times in any
|
331
406
|
# order.
|
407
|
+
# @return [PKCS11::Session]
|
332
408
|
def C_DigestKey(key)
|
333
409
|
@pk.C_DigestKey(@sess, key)
|
410
|
+
self
|
334
411
|
end
|
335
412
|
# Finishes a multiple-part message-digesting operation, returning the
|
336
413
|
# message digest as String.
|
414
|
+
#
|
415
|
+
# See {Session#digest} for convenience.
|
416
|
+
# @param [Integer, nil] out_size The buffer size for output data provided to the
|
417
|
+
# library. If nil, size is determined automatically.
|
418
|
+
# @return [String]
|
337
419
|
def C_DigestFinal(out_size=nil)
|
338
420
|
@pk.C_DigestFinal(@sess, out_size)
|
339
421
|
end
|
340
422
|
|
341
|
-
# Convenience method for the C_DigestInit, C_DigestUpdate, C_DigestKey,
|
342
|
-
# C_DigestFinal call flow.
|
423
|
+
# Convenience method for the {Session#C_DigestInit}, {Session#C_DigestUpdate}, {Session#C_DigestKey},
|
424
|
+
# {Session#C_DigestFinal} call flow.
|
343
425
|
#
|
344
|
-
#
|
426
|
+
# @example
|
345
427
|
# digest_string = session.digest( :SHA_1 ) do |cipher|
|
346
428
|
# cipher.update("key prefix")
|
347
429
|
# cipher.digest_key(some_key)
|
348
430
|
# end
|
431
|
+
# @param [Hash, Symbol, Integer, PKCS11::CK_MECHANISM] mechanism used mechanism
|
432
|
+
# @param [String] data data to digest
|
433
|
+
# @yield [PKCS11::Session::DigestCipher] Cipher object for processing data parts
|
434
|
+
# @return [String] final message digest
|
435
|
+
# @see Session#encrypt
|
349
436
|
def digest(mechanism, data=nil, &block)
|
350
437
|
C_DigestInit(mechanism)
|
351
438
|
if block_given?
|
@@ -364,27 +451,46 @@ module PKCS11
|
|
364
451
|
# Initializes a signature operation, where the signature is an appendix to the
|
365
452
|
# data.
|
366
453
|
#
|
367
|
-
# See Session#sign for convenience.
|
454
|
+
# See {Session#sign} for convenience.
|
455
|
+
# @return [PKCS11::Session]
|
368
456
|
def C_SignInit(mechanism, key)
|
369
|
-
@pk.C_SignInit(@sess,
|
457
|
+
@pk.C_SignInit(@sess, to_mechanism(mechanism), key)
|
458
|
+
self
|
370
459
|
end
|
371
460
|
# Signs data in a single part, where the signature is an appendix to the data.
|
461
|
+
#
|
462
|
+
# See {Session#sign} for convenience.
|
463
|
+
# @return [String] message signature
|
372
464
|
def C_Sign(data, out_size=nil)
|
373
465
|
@pk.C_Sign(@sess, data, out_size)
|
374
466
|
end
|
375
467
|
# Continues a multiple-part signature operation, processing another data
|
376
468
|
# part.
|
469
|
+
#
|
470
|
+
# See {Session#sign} for convenience.
|
471
|
+
# @return [PKCS11::Session]
|
377
472
|
def C_SignUpdate(data)
|
378
473
|
@pk.C_SignUpdate(@sess, data)
|
474
|
+
self
|
379
475
|
end
|
380
476
|
# Finishes a multiple-part signature operation, returning the signature.
|
477
|
+
#
|
478
|
+
# See {Session#sign} for convenience.
|
479
|
+
# @return [String] message signature
|
381
480
|
def C_SignFinal(out_size=nil)
|
382
481
|
@pk.C_SignFinal(@sess, out_size)
|
383
482
|
end
|
384
483
|
|
385
|
-
# Convenience method for the C_SignInit, C_SignUpdate, C_SignFinal call flow.
|
484
|
+
# Convenience method for the {Session#C_SignInit}, {Session#C_SignUpdate}, {Session#C_SignFinal} call flow.
|
386
485
|
#
|
387
|
-
#
|
486
|
+
# @param [Hash, Symbol, Integer, PKCS11::CK_MECHANISM] mechanism used mechanism
|
487
|
+
# @param [PKCS11::Object] key used key
|
488
|
+
# @param [String] data data to sign
|
489
|
+
# @yield [PKCS11::Session::Cipher] Cipher object for processing data parts
|
490
|
+
# @return [String] signature
|
491
|
+
# @see Session#encrypt
|
492
|
+
# @example Sign a text by a RSA private key
|
493
|
+
# signature = session.sign( :SHA1_RSA_PKCS, rsa_priv_key, "important text")
|
388
494
|
def sign(mechanism, key, data=nil, &block)
|
389
495
|
common_crypt(:C_SignInit, :C_SignUpdate, :C_SignFinal, :C_Sign,
|
390
496
|
mechanism, key, data, &block)
|
@@ -394,30 +500,43 @@ module PKCS11
|
|
394
500
|
# Initializes a verification operation, where the signature is an appendix to
|
395
501
|
# the data.
|
396
502
|
#
|
397
|
-
# See
|
503
|
+
# See {Session#verify} for convenience.
|
398
504
|
def C_VerifyInit(mechanism, key)
|
399
|
-
@pk.C_VerifyInit(@sess,
|
505
|
+
@pk.C_VerifyInit(@sess, to_mechanism(mechanism), key)
|
400
506
|
end
|
401
507
|
# Verifies a signature in a single-part operation, where the signature is an
|
402
508
|
# appendix to the data.
|
509
|
+
#
|
510
|
+
# See {Session#verify} for convenience.
|
403
511
|
def C_Verify(data, out_size=nil)
|
404
512
|
@pk.C_Verify(@sess, data, out_size)
|
405
513
|
end
|
406
514
|
# Continues a multiple-part verification operation, processing another
|
407
515
|
# data part.
|
516
|
+
#
|
517
|
+
# See {Session#verify} for convenience.
|
408
518
|
def C_VerifyUpdate(data)
|
409
519
|
@pk.C_VerifyUpdate(@sess, data)
|
410
520
|
end
|
411
521
|
# Finishes a multiple-part verification operation, checking the signature.
|
412
522
|
#
|
413
|
-
#
|
523
|
+
# See {Session#verify} for convenience.
|
524
|
+
# @return [Boolean] <tt>true</tt> for valid signature.
|
414
525
|
def C_VerifyFinal(out_size=nil)
|
415
526
|
@pk.C_VerifyFinal(@sess, out_size)
|
416
527
|
end
|
417
528
|
|
418
|
-
# Convenience method for the C_VerifyInit, C_VerifyUpdate, C_VerifyFinal call flow.
|
529
|
+
# Convenience method for the {Session#C_VerifyInit}, {Session#C_VerifyUpdate}, {Session#C_VerifyFinal} call flow.
|
419
530
|
#
|
420
|
-
#
|
531
|
+
# @see Session#encrypt
|
532
|
+
# @param [Hash, Symbol, Integer, PKCS11::CK_MECHANISM] mechanism used mechanism
|
533
|
+
# @param [PKCS11::Object] key used key
|
534
|
+
# @param [String] signature signature
|
535
|
+
# @param [String] data data to verify against signature
|
536
|
+
# @yield [PKCS11::Session::Cipher] Cipher object for processing data parts
|
537
|
+
# @return [Boolean] <tt>true</tt> for valid signature.
|
538
|
+
# @example
|
539
|
+
# raise("wrong signature") unless session.verify(:SHA1_RSA_PKCS, rsa_pub_key, signature, plaintext)
|
421
540
|
def verify(mechanism, key, signature, data=nil, &block)
|
422
541
|
common_verify(:C_VerifyInit, :C_VerifyUpdate, :C_VerifyFinal, :C_Verify,
|
423
542
|
mechanism, key, signature, data, &block)
|
@@ -425,16 +544,27 @@ module PKCS11
|
|
425
544
|
|
426
545
|
# Initializes a signature operation, where the data can be recovered
|
427
546
|
# from the signature
|
547
|
+
#
|
548
|
+
# See {Session#sign_recover} for convenience.
|
428
549
|
def C_SignRecoverInit(mechanism, key)
|
429
|
-
@pk.C_SignRecoverInit(@sess,
|
550
|
+
@pk.C_SignRecoverInit(@sess, to_mechanism(mechanism), key)
|
551
|
+
self
|
430
552
|
end
|
431
553
|
# Signs data in a single operation, where the data can be recovered from
|
432
554
|
# the signature.
|
555
|
+
#
|
556
|
+
# See {Session#sign_recover} for convenience.
|
433
557
|
def C_SignRecover(data, out_size=nil)
|
434
558
|
@pk.C_SignRecover(@sess, data, out_size)
|
435
559
|
end
|
436
560
|
|
437
|
-
# Convenience method for the C_SignRecoverInit, C_SignRecover call flow.
|
561
|
+
# Convenience method for the {Session#C_SignRecoverInit}, {Session#C_SignRecover} call flow.
|
562
|
+
#
|
563
|
+
# @param [Hash, Symbol, Integer, PKCS11::CK_MECHANISM] mechanism used mechanism
|
564
|
+
# @param [PKCS11::Object] key signature key
|
565
|
+
# @param [String] data data to be recovered
|
566
|
+
# @return [String] signature
|
567
|
+
# @see Session#verify_recover
|
438
568
|
def sign_recover(mechanism, key, data)
|
439
569
|
C_SignRecoverInit(mechanism, key)
|
440
570
|
C_SignRecover(data)
|
@@ -444,17 +574,24 @@ module PKCS11
|
|
444
574
|
# Initializes a signature verification operation, where the data can be recovered
|
445
575
|
# from the signature
|
446
576
|
#
|
447
|
-
# See Session#verify_recover for convenience.
|
577
|
+
# See {Session#verify_recover} for convenience.
|
448
578
|
def C_VerifyRecoverInit(mechanism, key)
|
449
|
-
@pk.C_VerifyRecoverInit(@sess,
|
579
|
+
@pk.C_VerifyRecoverInit(@sess, to_mechanism(mechanism), key)
|
450
580
|
end
|
451
581
|
# Verifies a signature in a single-part operation, where the data is
|
452
582
|
# recovered from the signature.
|
583
|
+
#
|
584
|
+
# See {Session#verify_recover} for convenience.
|
453
585
|
def C_VerifyRecover(signature, out_size=nil)
|
454
586
|
@pk.C_VerifyRecover(@sess, signature, out_size=nil)
|
455
587
|
end
|
456
588
|
|
457
|
-
# Convenience method for the C_VerifyRecoverInit, C_VerifyRecover call flow.
|
589
|
+
# Convenience method for the {Session#C_VerifyRecoverInit}, {Session#C_VerifyRecover} call flow.
|
590
|
+
#
|
591
|
+
# @param [Hash, Symbol, Integer, PKCS11::CK_MECHANISM] mechanism used mechanism
|
592
|
+
# @param [PKCS11::Object] key verification key
|
593
|
+
# @return [String] recovered data
|
594
|
+
# @see Session#sign_recover
|
458
595
|
def verify_recover(mechanism, key, signature)
|
459
596
|
C_VerifyRecoverInit(mechanism, key)
|
460
597
|
C_VerifyRecover(signature)
|
@@ -464,9 +601,9 @@ module PKCS11
|
|
464
601
|
# processing another data part.
|
465
602
|
#
|
466
603
|
# Digest and encryption operations must both be active (they must have been initialized
|
467
|
-
# with C_DigestInit and C_EncryptInit, respectively). This function may be called any
|
468
|
-
# number of times in succession, and may be interspersed with C_DigestUpdate,
|
469
|
-
# C_DigestKey, and C_EncryptUpdate calls.
|
604
|
+
# with {Session#C_DigestInit} and {Session#C_EncryptInit}, respectively). This function may be called any
|
605
|
+
# number of times in succession, and may be interspersed with {Session#C_DigestUpdate},
|
606
|
+
# {Session#C_DigestKey}, and {Session#C_EncryptUpdate} calls.
|
470
607
|
def C_DigestEncryptUpdate(data, out_size=nil)
|
471
608
|
@pk.C_DigestEncryptUpdate(@sess, data, out_size)
|
472
609
|
end
|
@@ -475,9 +612,9 @@ module PKCS11
|
|
475
612
|
# operation, processing another data part.
|
476
613
|
#
|
477
614
|
# Decryption and digesting operations must both be active (they must have been initialized
|
478
|
-
# with C_DecryptInit and C_DigestInit, respectively). This function may be called any
|
479
|
-
# number of times in succession, and may be interspersed with C_DecryptUpdate,
|
480
|
-
# C_DigestUpdate, and C_DigestKey calls.
|
615
|
+
# with {Session#C_DecryptInit} and {Session#C_DigestInit}, respectively). This function may be called any
|
616
|
+
# number of times in succession, and may be interspersed with {Session#C_DecryptUpdate},
|
617
|
+
# {Session#C_DigestUpdate}, and {Session#C_DigestKey} calls.
|
481
618
|
def C_DecryptDigestUpdate(data, out_size=nil)
|
482
619
|
@pk.C_DecryptDigestUpdate(@sess, data, out_size)
|
483
620
|
end
|
@@ -486,9 +623,9 @@ module PKCS11
|
|
486
623
|
# operation, processing another data part.
|
487
624
|
#
|
488
625
|
# Signature and encryption operations must both be active (they must have been initialized
|
489
|
-
# with C_SignInit and C_EncryptInit, respectively). This function may be called any
|
490
|
-
# number of times in succession, and may be interspersed with C_SignUpdate and
|
491
|
-
# C_EncryptUpdate calls.
|
626
|
+
# with {Session#C_SignInit} and {Session#C_EncryptInit}, respectively). This function may be called any
|
627
|
+
# number of times in succession, and may be interspersed with {Session#C_SignUpdate} and
|
628
|
+
# {Session#C_EncryptUpdate} calls.
|
492
629
|
def C_SignEncryptUpdate(data, out_size=nil)
|
493
630
|
@pk.C_SignEncryptUpdate(@sess, data, out_size)
|
494
631
|
end
|
@@ -497,9 +634,9 @@ module PKCS11
|
|
497
634
|
# verification operation, processing another data part.
|
498
635
|
#
|
499
636
|
# Decryption and signature operations must both be active (they must have been initialized
|
500
|
-
# with C_DecryptInit and C_VerifyInit, respectively). This function may be called any
|
501
|
-
# number of times in succession, and may be interspersed with C_DecryptUpdate and
|
502
|
-
# C_VerifyUpdate calls.
|
637
|
+
# with {Session#C_DecryptInit} and {Session#C_VerifyInit}, respectively). This function may be called any
|
638
|
+
# number of times in succession, and may be interspersed with {Session#C_DecryptUpdate} and
|
639
|
+
# {Session#C_VerifyUpdate} calls.
|
503
640
|
def C_DecryptVerifyUpdate(data, out_size=nil)
|
504
641
|
@pk.C_DecryptVerifyUpdate(@sess, data, out_size)
|
505
642
|
end
|
@@ -507,62 +644,122 @@ module PKCS11
|
|
507
644
|
# Generates a secret key Object or set of domain parameters, creating a new
|
508
645
|
# Object.
|
509
646
|
#
|
510
|
-
#
|
647
|
+
# @param [Hash, Symbol, Integer, PKCS11::CK_MECHANISM] mechanism used mechanism
|
648
|
+
# @param [Hash] template Attributes of the key to create.
|
649
|
+
# @return [PKCS11::Object] key Object of the new created key.
|
650
|
+
# @example generate 112 bit DES key
|
651
|
+
# key = session.generate_key(:DES2_KEY_GEN,
|
652
|
+
# {:ENCRYPT=>true, :WRAP=>true, :DECRYPT=>true, :UNWRAP=>true})
|
511
653
|
def C_GenerateKey(mechanism, template={})
|
512
|
-
obj = @pk.C_GenerateKey(@sess,
|
654
|
+
obj = @pk.C_GenerateKey(@sess, to_mechanism(mechanism), to_attributes(template))
|
513
655
|
Object.new @pk, @sess, obj
|
514
656
|
end
|
515
657
|
alias generate_key C_GenerateKey
|
516
658
|
|
517
659
|
# Generates a public/private key pair, creating new key Object instances.
|
518
660
|
#
|
519
|
-
#
|
661
|
+
# @param [Hash, Symbol, Integer, PKCS11::CK_MECHANISM] mechanism used mechanism
|
662
|
+
# @param [Hash] pubkey_template Attributes of the public key to create.
|
663
|
+
# @param [Hash] privkey_template Attributes of the private key to create.
|
664
|
+
# @return [Array<PKCS11::Object>] an two-items array of new created public and private key Object.
|
665
|
+
# @example
|
666
|
+
# pub_key, priv_key = session.generate_key_pair(:RSA_PKCS_KEY_PAIR_GEN,
|
667
|
+
# {:ENCRYPT=>true, :VERIFY=>true, :WRAP=>true, :MODULUS_BITS=>768, :PUBLIC_EXPONENT=>3},
|
668
|
+
# {:SUBJECT=>'test', :ID=>"ID", :DECRYPT=>true, :SIGN=>true, :UNWRAP=>true})
|
520
669
|
def C_GenerateKeyPair(mechanism, pubkey_template={}, privkey_template={})
|
521
|
-
objs = @pk.C_GenerateKeyPair(@sess,
|
670
|
+
objs = @pk.C_GenerateKeyPair(@sess, to_mechanism(mechanism), to_attributes(pubkey_template), to_attributes(privkey_template))
|
522
671
|
objs.map{|obj| Object.new @pk, @sess, obj }
|
523
672
|
end
|
524
673
|
alias generate_key_pair C_GenerateKeyPair
|
525
674
|
|
526
675
|
# Wraps (i.e., encrypts) a private or secret key.
|
527
676
|
#
|
528
|
-
#
|
677
|
+
# @param [Hash, Symbol, Integer, PKCS11::CK_MECHANISM] mechanism used mechanism
|
678
|
+
# @param [PKCS11::Object] wrapping_key wrapping key
|
679
|
+
# @param [PKCS11::Object] wrapped_key key to wrap
|
680
|
+
# @return [String] the encrypted binary data.
|
681
|
+
# @see Session#C_UnwrapKey
|
682
|
+
# @example Wrapping a secret key
|
683
|
+
# wrapped_key_value = session.wrap_key(:DES3_ECB, secret_key, secret_key)
|
684
|
+
# @example Wrapping a private key
|
685
|
+
# wrapped_key_value = session.wrap_key({:DES3_CBC_PAD=>"\0"*8}, secret_key, rsa_priv_key)
|
529
686
|
def C_WrapKey(mechanism, wrapping_key, wrapped_key, out_size=nil)
|
530
|
-
@pk.C_WrapKey(@sess,
|
687
|
+
@pk.C_WrapKey(@sess, to_mechanism(mechanism), wrapping_key, wrapped_key, out_size)
|
531
688
|
end
|
532
689
|
alias wrap_key C_WrapKey
|
533
690
|
|
534
691
|
# Unwraps (i.e. decrypts) a wrapped key, creating a new private key or
|
535
692
|
# secret key object.
|
536
693
|
#
|
537
|
-
#
|
694
|
+
# @param [Hash, Symbol, Integer, PKCS11::CK_MECHANISM] mechanism used mechanism
|
695
|
+
# @param [PKCS11::Object] wrapping_key wrapping key
|
696
|
+
# @param [String] wrapped_key key data of the wrapped key
|
697
|
+
# @return [PKCS11::Object] key object of the new created key.
|
698
|
+
# @see Session#C_WrapKey
|
699
|
+
# @example
|
700
|
+
# unwrapped_key = session.unwrap_key(:DES3_ECB, secret_key, wrapped_key_value,
|
701
|
+
# :CLASS=>CKO_SECRET_KEY, :KEY_TYPE=>CKK_DES2, :ENCRYPT=>true, :DECRYPT=>true)
|
538
702
|
def C_UnwrapKey(mechanism, wrapping_key, wrapped_key, template={})
|
539
|
-
obj = @pk.C_UnwrapKey(@sess,
|
703
|
+
obj = @pk.C_UnwrapKey(@sess, to_mechanism(mechanism), wrapping_key, wrapped_key, to_attributes(template))
|
540
704
|
Object.new @pk, @sess, obj
|
541
705
|
end
|
542
706
|
alias unwrap_key C_UnwrapKey
|
543
707
|
|
544
708
|
# Derives a key from a base key, creating a new key object.
|
545
709
|
#
|
546
|
-
#
|
710
|
+
# @param [Hash, Symbol, Integer, PKCS11::CK_MECHANISM] mechanism used mechanism
|
711
|
+
# @param [PKCS11::Object] base_key key to derive
|
712
|
+
# @param [Hash] template Attributes of the object to create.
|
713
|
+
# @return [PKCS11::Object] key object of the new created key.
|
714
|
+
# @example Derive a AES key by XORing with some derivation data
|
715
|
+
# deriv_data = "\0"*16
|
716
|
+
# new_key = session.derive_key( {CKM_XOR_BASE_AND_DATA => {:pData => deriv_data}}, secret_key,
|
717
|
+
# :CLASS=>CKO_SECRET_KEY, :KEY_TYPE=>CKK_AES, :VALUE_LEN=>16, :ENCRYPT=>true )
|
547
718
|
def C_DeriveKey(mechanism, base_key, template={})
|
548
|
-
obj = @pk.C_DeriveKey(@sess,
|
719
|
+
obj = @pk.C_DeriveKey(@sess, to_mechanism(mechanism), base_key, to_attributes(template))
|
549
720
|
Object.new @pk, @sess, obj
|
550
721
|
end
|
551
722
|
alias derive_key C_DeriveKey
|
552
723
|
|
553
|
-
# Mixes additional seed material into the token
|
724
|
+
# Mixes additional seed material into the token's random number
|
554
725
|
# generator.
|
726
|
+
# @param [String] entropy data
|
727
|
+
# @return [PKCS11::Session]
|
555
728
|
def C_SeedRandom(data)
|
556
729
|
@pk.C_SeedRandom(@sess, data)
|
730
|
+
self
|
557
731
|
end
|
558
732
|
alias seed_random C_SeedRandom
|
559
733
|
|
560
734
|
# Generates random or pseudo-random data.
|
561
735
|
#
|
562
|
-
#
|
736
|
+
# @param [Integer] out_size
|
737
|
+
# @return [String] random or pseudo-random binary data of <tt>out_size</tt> bytes.
|
563
738
|
def C_GenerateRandom(out_size)
|
564
739
|
@pk.C_GenerateRandom(@sess, out_size)
|
565
740
|
end
|
566
741
|
alias generate_random C_GenerateRandom
|
742
|
+
|
743
|
+
# Obtains a copy of the cryptographic operations state of a session,
|
744
|
+
# encoded as a string of bytes.
|
745
|
+
# @return [String]
|
746
|
+
# @see Session#C_SetOperationState
|
747
|
+
def C_GetOperationState
|
748
|
+
@pk.C_GetOperationState(@sess)
|
749
|
+
end
|
750
|
+
alias get_operation_state C_GetOperationState
|
751
|
+
|
752
|
+
# Restores the cryptographic operations state of a session from a
|
753
|
+
# string of bytes obtained with {Session#C_GetOperationState}.
|
754
|
+
#
|
755
|
+
# @param [String] state previously stored session state
|
756
|
+
# @param [PKCS11::Object] encryption key for sessions stored without keys
|
757
|
+
# @param [PKCS11::Object] authentication key for sessions stored without keys
|
758
|
+
# @return [PKCS11::Session]
|
759
|
+
def C_SetOperationState(state, enc_key=nil, auth_key=nil)
|
760
|
+
@pk.C_SetOperationState(@sess, state, enc_key||0, auth_key||0)
|
761
|
+
self
|
762
|
+
end
|
763
|
+
alias set_operation_state C_SetOperationState
|
567
764
|
end
|
568
765
|
end
|