[Security-sig] Unified TLS API for Python

Nathaniel Smith njs at pobox.com
Fri Jan 13 04:07:42 EST 2017


On Fri, Jan 13, 2017 at 12:55 AM, Cory Benfield <cory at lukasa.co.uk> wrote:
>
>> On 13 Jan 2017, at 08:48, Nathaniel Smith <njs at pobox.com> wrote:
>>
>> The potentially-useful idea I took from this subthread was: right now
>> we have an interface with a bunch of getter/setter methods. Would it
>> make sense to *replace* that with something more declarative, like a
>> single method that takes configuration data in some sort of standard
>> format? Something very non-clever and simple, like, a dict? That seems
>> like it might both simplify the interface (e.g. the sni callback can
>> return one of these dicts instead of trying to specify OpenSSL's
>> context switcheroo weirdness; if the new dict tries to change options
>> that this particular implementation doesn't allow to be changed, then
>> it's free to error out) and be more flexible (e.g. people could write
>> validation routines that take one of these dicts and raise an error or
>> not -- having a standard format enables this but also makes it not our
>> problem).
>
> Hmm.
>
> I am extremely unsure. At the risk of sounding facetious, these two solutions are *basically* isomorphic to one another: that is, an object with a bunch of getters/setters is basically a dict, it’s just not something you can shove arbitrary data into.
>
> That’s my main reason for preferring an object to a dict: I want to eliminate a whole class of bugs that comes from accidentally doing `config[’ssl_version_minimum’] = TLSv1_1` instead of `config[‘tls_version_minimum’] = TLSv1_1`. Put another way, the dict approach either requires that we override __setitem__ to validate all keys, or that we allow typo-based errors where people accidentally get the default. The second is terrifying, so we’d have to do the first, and at that point we’re basically just writing an object. ;)
>
> I think the actual key here is that the “Context” object should not be confused with an *actual* Context object from the backing library. That is, a ClientContext() for OpenSSL does not need to compose onto it a SSL_CTX from OpenSSL. It is quite reasonable for the ClientContext to just be a bucket of data that manufactures SSL_CTX objects when wrap_socket is called.
>
> I don’t see why validation routines couldn’t run on Context objects directly. You could even do this:
>
> class ClientValidationContext(ClientContext):
>     def validate(self):
>         “””Checks internal state and returns whether or not the context meets your criteria”””
>         pass
>
> Essentially, because all code operates on the abstract objects, one of the things you can do for validation is insert a *validating* implementation of the abstract object. It’s exactly like a test fake, but for validation.
>
> Does that make any sense, or have I not had enough coffee yet?

I was imagining something along the lines of

ClientContext({...}).wrap_socket(...)

where ClientContext.__init__ would do the validation. Or there are
various other ways to slot the pieces together, but in general
validation doesn't need to be done incrementally; there's going to be
some place where the config dict hits the library and it can/should be
validated at that point. And before that point we get all the dict
niceties for free.

-n

-- 
Nathaniel J. Smith -- https://vorpus.org


More information about the Security-SIG mailing list