1313
1414@six .add_metaclass (_utils .CheckLastError )
1515class SecurityContext (rsec_contexts .SecurityContext ):
16+ """A GSSAPI Security Context
17+
18+ This class represents a GSSAPI security context that may be used
19+ with and/or returned by other GSSAPI methods.
20+
21+ It inherits from the low-level GSSAPI
22+ :class:`~gssapi.raw.sec_contexts.SecurityContext` class,
23+ and thus may used with both low-level and high-level API methods.
24+
25+ This class may be pickled an unpickled.
26+ """
27+
1628 def __new__ (cls , base = None , token = None ,
1729 name = None , creds = None , desired_lifetime = None , flags = None ,
1830 mech_type = None , channel_bindings = None , usage = None ):
@@ -25,6 +37,31 @@ def __new__(cls, base=None, token=None,
2537 def __init__ (self , base = None , token = None ,
2638 name = None , creds = None , desired_lifetime = None , flags = None ,
2739 mech_type = None , channel_bindings = None , usage = None ):
40+ """
41+ The constructor creates a new security context, but does not begin
42+ the initiate or accept process.
43+
44+ If the `base` argument is used, an existing
45+ :class:`~gssapi.raw.sec_contexts.SecurityContext` object from
46+ the low-level API is converted into a high-level object.
47+
48+ If the `token` argument is passed, the security context is imported
49+ using the token.
50+
51+ Otherwise, a new security context is created.
52+
53+ If the `usage` argument is not passed, the constructor will attempt
54+ to detect what the appropriate usage is based on either the existing
55+ security context (if `base` or `token` are used) or the argument set.
56+
57+ For a security context of the `initiate` usage, the `name` argument
58+ must be used, and the `creds`, `mech_type`, `flags`,
59+ `desired_lifetime`, and `channel_bindings` arguments may be
60+ used as well.
61+
62+ For a security context of the `accept` usage, the `creds` and
63+ `channel_bindings` arguments may optionally be used.
64+ """
2865
2966 # NB(directxman12): _last_err must be set first
3067 self ._last_err = None
@@ -84,19 +121,95 @@ def __init__(self, base=None, token=None,
84121 # TODO(directxman12): implement flag properties
85122
86123 def get_signature (self , message ):
124+ """Calculate the signature for a message
125+
126+ This method calculates the signature (called a MIC) for
127+ the given message, which may be then used with
128+ :meth:`verify_signature` to confirm the validity of the
129+ signature. This is useful if you wish to transmit the
130+ message signature and message in your own format.
131+
132+ Args:
133+ message (bytes): the input message
134+
135+ Returns:
136+ bytes: the message signature
137+ """
138+
87139 # TODO(directxman12): check flags?
88140 return rmessage .get_mic (self , message )
89141
90142 def verify_signature (self , message , mic ):
143+ """Verify the signature for a message
144+
145+ This method verifies that a signature (generated by
146+ :meth:`get_signature` is valid for the given method.
147+
148+ If the signature is valid, the method will return.
149+ Otherwise, it will raise an error.
150+
151+ Args:
152+ message (bytes): the message
153+ mic (bytes): the signature to verify
154+
155+ Raises:
156+ BadMICError: the signature was not valid
157+ """
158+
91159 return rmessage .verify_mic (self , message , mic )
92160
93161 def wrap (self , message , encrypt ):
162+ """Wrap a message, optionally with encryption
163+
164+ This method generates a signature and uses it to
165+ wrap the message, optionally encrypting it.
166+
167+ Args:
168+ message (bytes): the message to wrap
169+ encrypt (bool): whether or not to encrypt the message
170+
171+ Returns:
172+ WrapResult: the wrapped message and details about it
173+ (e.g. whether encryption was used succesfully)
174+ """
175+
94176 return rmessage .wrap (self , message , encrypt )
95177
96178 def unwrap (self , message ):
179+ """Unwrap a wrapped message
180+
181+ This method unwraps/unencrypts a wrapped message,
182+ verifying the signature along the way.
183+
184+ Args:
185+ message (bytes): the message to unwrap/decrypt
186+
187+ Returns:
188+ UnwrapResult: the unwrapped message and details about it
189+ (e.g. wheter encryption was used)
190+ """
191+
97192 return rmessage .unwrap (self , message )
98193
99194 def encrypt (self , message ):
195+ """Encrypt a message
196+
197+ This method wraps and encrypts a message, similarly to
198+ :meth:`wrap`. The difference is that encryption is always
199+ used, and the method will raise an exception if this is
200+ not possible. Additionally, this method simply returns
201+ the encrypted message directly.
202+
203+ Args:
204+ message (bytes): the message to encrypt
205+
206+ Returns:
207+ bytes: the encrypted message
208+
209+ Raises:
210+ EncryptionNotUsed: the encryption could not be used
211+ """
212+
100213 res = self .wrap (message , encrypt = True )
101214
102215 if not res .encrypted :
@@ -105,6 +218,23 @@ def encrypt(self, message):
105218 return res .message
106219
107220 def decrypt (self , message ):
221+ """Decrypt a message
222+
223+ This method decrypts and unwraps a message, verifying the signature
224+ along the way, similarly to :meth:`unwrap`. The difference is that
225+ this method will raise an exception if encryption was by the context
226+ and not used, and simply returns the decrypted message directly.
227+
228+ Args:
229+ message (bytes): the encrypted message
230+
231+ Returns:
232+ bytes: the decrypted message
233+
234+ Raises:
235+ EncryptionNotUsed: encryption was expected, but not used
236+ """
237+
108238 res = self .unwrap (message )
109239
110240 if (not res .encrypted and
@@ -118,26 +248,80 @@ def decrypt(self, message):
118248
119249 def get_wrap_size_limit (self , desired_output_size ,
120250 encrypted = True ):
251+ """Get the maximum message size for a given wrapped message size
252+
253+ This method calculates the maximum input message size for a given
254+ wrapped/encrypted message size.
255+
256+ Args:
257+ desired_output_size (int): the maximum output message size
258+ encrypted (bool): whether or not encryption should be taken
259+ into account
260+
261+ Returns:
262+ int: the maximum input message size
263+ """
264+
121265 return rmessage .wrap_size_limit (self , desired_output_size ,
122266 encrypted )
123267
124268 def process_token (self , token ):
269+ """Process an output token asynchronously
270+
271+ This method processes an output token even when the security context
272+ was not expecting it.
273+
274+ Args:
275+ token (bytes): the token to process
276+ """
277+
125278 rsec_contexts .process_context_token (self , token )
126279
127280 def export (self ):
281+ """Export a security context
282+
283+ This method exports a security context, allowing it to be passed
284+ between processes.
285+
286+ Returns:
287+ bytes: the exported security context
288+ """
289+
128290 return rsec_contexts .export_sec_context (self )
129291
130- INQUIRE_ARGS = ('initiator_name' , 'target_name' , 'lifetime' ,
131- 'mech_type' , 'flags' , 'locally_init' , 'complete' )
292+ _INQUIRE_ARGS = ('initiator_name' , 'target_name' , 'lifetime' ,
293+ 'mech_type' , 'flags' , 'locally_init' , 'complete' )
132294
133295 @_utils .check_last_err
134296 def _inquire (self , ** kwargs ):
297+ """Inspect the security context for information
298+
299+ This method inspects the security context for information.
300+
301+ If no keyword arguments are passed, all available information
302+ is returned. Otherwise, only the keyword arguments that
303+ are passed and set to `True` are returned.
304+
305+ Args:
306+ initiator_name (bool): get the initiator name for this context
307+ target_name (bool): get the target name for this context
308+ lifetime (bool): get the remaining lifetime for this context
309+ mech_type (bool): get the mechanism used by this context
310+ flags (bool): get the flags set on this context
311+ locally_init (bool): get whether this context was locally initiated
312+ complete (bool): get whether negotiation on this context has
313+ been completed
314+
315+ Returns:
316+ InquireContextResult: the results of the inquiry, with unused
317+ fields set to None
318+ """
135319 if not kwargs :
136320 default_val = True
137321 else :
138322 default_val = False
139323
140- for arg in self .INQUIRE_ARGS :
324+ for arg in self ._INQUIRE_ARGS :
141325 kwargs [arg ] = kwargs .get (arg , default_val )
142326
143327 res = rsec_contexts .inquire_context (self , ** kwargs )
@@ -161,24 +345,58 @@ def _inquire(self, **kwargs):
161345
162346 @property
163347 def lifetime (self ):
348+ """Get the amount of time for which the context remains valid"""
164349 return rsec_contexts .context_time (self )
165350
166- initiator_name = _utils .inquire_property ('initiator_name' )
167- target_name = _utils .inquire_property ('target_name' )
168- mech_type = _utils .inquire_property ('mech_type' )
169- actual_flags = _utils .inquire_property ('flags' )
170- locally_initiated = _utils .inquire_property ('locally_init' )
351+ initiator_name = _utils .inquire_property (
352+ 'initiator_name' , 'Get the Name of the initiator of this context' )
353+ target_name = _utils .inquire_property (
354+ 'target_name' , 'Get the Name of the target of this context' )
355+ mech_type = _utils .inquire_property (
356+ 'mech_type' , 'Get the mechanism in use by this context' )
357+ actual_flags = _utils .inquire_property (
358+ 'flags' , 'Get the flags set on this context' )
359+ locally_initiated = _utils .inquire_property (
360+ 'locally_init' , 'Get whether this context was locally intiated' )
171361
172362 @property
173363 @_utils .check_last_err
174364 def complete (self ):
365+ """Get whether negotiation for this context has been completed"""
175366 if self ._started :
176367 return self ._inquire (complete = True ).complete
177368 else :
178369 return False
179370
180371 @_utils .catch_and_return_token
181372 def step (self , token = None ):
373+ """Perform a negotation step
374+
375+ This method performs a negotiation step based on the usage type
376+ of this context. If `__DEFER_STEP_ERRORS__` is set to true,
377+ this method will return a token, even when exceptions would be
378+ thrown. The generated exception will be thrown on the next
379+ method call or property lookup on the context.
380+
381+ This method should be used in a while loop, as such:
382+
383+ .. code-block:: python
384+
385+ input_token = None
386+ try:
387+ while not ctx.complete:
388+ output_token = ctx.step(input_token)
389+ input_token = send_and_receive(output_token)
390+ except GSSError as e:
391+ handle_the_issue()
392+
393+ Args:
394+ token (bytes): the input token from the other participant's step
395+
396+ Returns:
397+ bytes: the output token to send to the other participant
398+ """
399+
182400 if self .usage == 'accept' :
183401 return self ._acceptor_step (token = token )
184402 else :
0 commit comments