Skip to content

SSL IntFlag enums VerifyFlags and Options are not continuous and contain "false" aliases #150577

@jbdyn

Description

@jbdyn

I noticed that the IntFlag enums VerifyFlags and Options from the ssl module are not continuous and contain aliases to (partially) absent members, i.e. "false" aliases.

This gives inconsistencies in flag iteration:

  • overshooting

    VerifyFlags

    from ssl import VerifyFlags
    print(~VerifyFlags(0))  # common for "not no flags", i.e. all flags
    # <VerifyFlags.VERIFY_CRL_CHECK_LEAF|VERIFY_X509_STRICT|VERIFY_ALLOW_PROXY_CERTS|VERIFY_X509_TRUSTED_FIRST|VERIFY_X509_PARTIAL_CHAIN|VERIFY_CRL_CHECK_CHAIN|491411: 1048575>
    # with added `491411`

    Options

    from ssl import Options
    print(~Options(0))  # common for "not no flags", i.e. all flags
    # <Options.OP_LEGACY_SERVER_CONNECT|OP_ENABLE_KTLS|OP_IGNORE_UNEXPECTED_EOF|OP_NO_TICKET|OP_NO_COMPRESSION|OP_ENABLE_MIDDLEBOX_COMPAT|OP_CIPHER_SERVER_PREFERENCE|OP_NO_SSLv3|OP_NO_TLSv1|OP_NO_TLSv1_2|OP_NO_TLSv1_1|OP_NO_TLSv1_3|OP_NO_RENEGOTIATION|OP_ALL|28163875: 4294967295>
    # with added `28163875`
  • false aliases

    VerifyFlags

    from ssl import VerifyFlags
    print(list(VerifyFlags.VERIFY_CRL_CHECK_CHAIN))  # with value `12`
    # [<VerifyFlags.VERIFY_CRL_CHECK_LEAF: 4>, None]
    # member with value `8` is absent from `VerifyFlags`

    Options

    from ssl import Options
    print(list(Options.OP_ALL))
    # [None, None, None]
    # no foundational members in `Options` at all
  • missing members due to being false aliases

    VerifyFlags

    from ssl import VerifyFlags
    
    all_members = set(VerifyFlags.__members__.values())
    iterated_members = set(VerifyFlags)
    
    print(all_members - iterated_members)
    # {<VerifyFlags.VERIFY_DEFAULT: 0>, <VerifyFlags.VERIFY_CRL_CHECK_CHAIN: 12>}
    # value `0` is trivial, but `VERIFY_CRL_CHECK_CHAIN` actually needs to be standalone (see previous bullet point)

    and consequently

    from ssl import VerifyFlags
    from functools import reduce
    from operator import or_
    
    all = reduce(or_, VerifyFlags)  # actually not all flags
    print(all)
    # <VerifyFlags.VERIFY_CRL_CHECK_LEAF|VERIFY_X509_STRICT|VERIFY_ALLOW_PROXY_CERTS|VERIFY_X509_TRUSTED_FIRST|VERIFY_X509_PARTIAL_CHAIN: 557156>
    # missing false alias `VERIFY_CRL_CHECK_CHAIN`
    
    print(sum(set(VerifyFlags.__members__.values())))
    # 557168
    # missing the `VERIFY_CRL_CHECK_CHAIN` with value `12`

    Options

    from ssl import Options
    
    all_members = set(Options.__members__.values())
    iterated_members = set(Options)
    
    print(all_members - iterated_members)
    # {<Options.OP_NO_SSLv2: 0>, <Options.OP_ALL: 2147483728>}
    # value `0` is trivial, but `OP_ALL` actually needs to be standalone (see previous bullet point)

    and resulting implications analogous to VerifyFlags.

With that, enum.show_flag_values also cannot really be used anymore.

I found these related issues:

The behavior shown above kind of collides with the achievement for simplicity mentioned in #72212 - and also confused me heavily.

@tiran @ethanfurman What do you think?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions