How to change a TLS context option

2019-08-23 04:38发布

问题:

In python, we can specify some TLS context options. For example, this code from the documentation here:

client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
client_context.options |= ssl.OP_NO_TLSv1
client_context.options |= ssl.OP_NO_TLSv1_1

I do not get this symbol |=. I read what it means but don't get why we use it here? why don't we use = ? Should I use |= to set any option? strangely, I find some examples also int he documentation use &=:

ctx = ssl.create_default_context(Purpose.CLIENT_AUTH)
ctx.options &= ~ssl.OP_NO_SSLv3

I need to specify another option. I jsut need to disable session ticket which is from this option:

ssl.OP_NO_TICKET

If I have context ctx, how to set ssl.OP_NO_TICKET? Should I use = or |= or &=? please explain.

回答1:

Each option is, in fact, a flag among many possible ones, so you need to compose them using bitwise AND (&) and bitwise OR (|) operations. It is done like that because these options are not mutually excluding each other, you need to compose a final value by picking various options that you combine together. So each one has a value being a power of 2, which means it is a bit being 1 at some specific position, and the final value then encodes if each specific separate flag is either on or off.

So you need bitwise operators to manage them and construct the final value you want.

See:

In [8]: print ssl.OP_NO_TLSv1, bin(ssl.OP_NO_TLSv1)
67108864 0b100000000000000000000000000

In [9]: print ssl.OP_NO_TLSv1_1, bin(ssl.OP_NO_TLSv1_1)
268435456 0b10000000000000000000000000000

In [13]: print ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1, bin(ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1)
335544320 0b10100000000000000000000000000

In [14]: print ssl.OP_NO_TLSv1 & ssl.OP_NO_TLSv1_1, bin(ssl.OP_NO_TLSv1 & ssl.OP_NO_TLSv1_1)
0 0b0

You see that if you want both of these options, you need to flip both bits to 1, and hence need an OR (|) otherwise with an AND (&) since each value has only one bit set to 1, at a different position each time, you are guaranteed to always get 0 as a result, which means no feature at all, so certainly not what you need.

In short, in cases like that to compose values, you will never use AND (&).

Now, about &= ~: ~ is the bitwise negation, so it is useful to remove some options while keeping other options that are already set.

ctx.options &= ~ssl.OP_NO_SSLv3

This construct makes you flip to 0 the bit related to ssl.OP_NO_SSLv3 in the final value.

See:

In [34]: ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)

In [37]: print ctx.options, bin(ctx.options)
2197816319 0b10000011000000000000001111111111

In [38]: print bin(ssl.OP_NO_SSLv3)
0b10000000000000000000000000

In [40]: print ctx.options & ~ssl.OP_NO_SSLv3
2164261887

In [41]: print ctx.options & ~ssl.OP_NO_SSLv3, bin(ctx.options & ~ssl.OP_NO_SSLv3)
2164261887 0b10000001000000000000001111111111

If you compare ctx.options and ctx.options & ~ssl.OP_NO_SSLv3 you will see that one bit has flipped from 1 to 0, because you in fact removed feature OP_NO_SSLv3.

If I have context ctx, how to set ssl.OP_NO_TICKET? Should I use = or |= or &=? please explain.

This is an option you want to add to all other ones you already have, so you do not want to loose them. Hence you need a bitwise OR (|).

  • if you do just = you set this option but lose all current other ones that have been set, so not what you need.
  • if you do |= you flip to 1 the bit related to the option you want, and you do not touch the other bits; this is what you want!
  • if you do &= you flip to 1 only those bits being both in your new value and the existing one, which means the result here can only be 0 if the value was not set already or the same exact value if it has been set:

(my example is done with another value that OP_NO_TICKET because I do not have it there, but the behaviour will be the same with any one, as each OP_ value is 2n, that is one bit to one and all others to 0)

In [16]: ctx = ssl.OP_NO_TLSv1

In [17]: print ctx, bin(ctx)
67108864 0b100000000000000000000000000

In [19]: ctx = ssl.OP_NO_TLSv1_1

In [20]: print ctx, bin(ctx)
268435456 0b10000000000000000000000000000

In [21]: ctx = ssl.OP_NO_TLSv1

In [22]: ctx |= ssl.OP_NO_TLSv1_1

In [23]: print ctx, bin(ctx)
335544320 0b10100000000000000000000000000

In [24]: ctx = ssl.OP_NO_TLSv1

In [25]: ctx &= ssl.OP_NO_TLSv1_1

In [26]: print ctx, bin(ctx)
0 0b0

Note how both bits are flipped to 1 in the case of |.