mirror of
https://github.com/thegeeklab/ansible-later.git
synced 2024-09-21 00:42:56 +02:00
655 lines
23 KiB
Python
655 lines
23 KiB
Python
|
# This file is dual licensed under the terms of the Apache License, Version
|
||
|
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
||
|
# for complete details.
|
||
|
|
||
|
from __future__ import absolute_import, division, print_function
|
||
|
|
||
|
import calendar
|
||
|
import ipaddress
|
||
|
|
||
|
import six
|
||
|
|
||
|
from cryptography import utils, x509
|
||
|
from cryptography.hazmat.backends.openssl.decode_asn1 import (
|
||
|
_CRL_ENTRY_REASON_ENUM_TO_CODE, _DISTPOINT_TYPE_FULLNAME,
|
||
|
_DISTPOINT_TYPE_RELATIVENAME
|
||
|
)
|
||
|
from cryptography.x509.name import _ASN1Type
|
||
|
from cryptography.x509.oid import (
|
||
|
CRLEntryExtensionOID, ExtensionOID, OCSPExtensionOID,
|
||
|
)
|
||
|
|
||
|
|
||
|
def _encode_asn1_int(backend, x):
|
||
|
"""
|
||
|
Converts a python integer to an ASN1_INTEGER. The returned ASN1_INTEGER
|
||
|
will not be garbage collected (to support adding them to structs that take
|
||
|
ownership of the object). Be sure to register it for GC if it will be
|
||
|
discarded after use.
|
||
|
|
||
|
"""
|
||
|
# Convert Python integer to OpenSSL "bignum" in case value exceeds
|
||
|
# machine's native integer limits (note: `int_to_bn` doesn't automatically
|
||
|
# GC).
|
||
|
i = backend._int_to_bn(x)
|
||
|
i = backend._ffi.gc(i, backend._lib.BN_free)
|
||
|
|
||
|
# Wrap in an ASN.1 integer. Don't GC -- as documented.
|
||
|
i = backend._lib.BN_to_ASN1_INTEGER(i, backend._ffi.NULL)
|
||
|
backend.openssl_assert(i != backend._ffi.NULL)
|
||
|
return i
|
||
|
|
||
|
|
||
|
def _encode_asn1_int_gc(backend, x):
|
||
|
i = _encode_asn1_int(backend, x)
|
||
|
i = backend._ffi.gc(i, backend._lib.ASN1_INTEGER_free)
|
||
|
return i
|
||
|
|
||
|
|
||
|
def _encode_asn1_str(backend, data):
|
||
|
"""
|
||
|
Create an ASN1_OCTET_STRING from a Python byte string.
|
||
|
"""
|
||
|
s = backend._lib.ASN1_OCTET_STRING_new()
|
||
|
res = backend._lib.ASN1_OCTET_STRING_set(s, data, len(data))
|
||
|
backend.openssl_assert(res == 1)
|
||
|
return s
|
||
|
|
||
|
|
||
|
def _encode_asn1_utf8_str(backend, string):
|
||
|
"""
|
||
|
Create an ASN1_UTF8STRING from a Python unicode string.
|
||
|
This object will be an ASN1_STRING with UTF8 type in OpenSSL and
|
||
|
can be decoded with ASN1_STRING_to_UTF8.
|
||
|
"""
|
||
|
s = backend._lib.ASN1_UTF8STRING_new()
|
||
|
res = backend._lib.ASN1_STRING_set(
|
||
|
s, string.encode("utf8"), len(string.encode("utf8"))
|
||
|
)
|
||
|
backend.openssl_assert(res == 1)
|
||
|
return s
|
||
|
|
||
|
|
||
|
def _encode_asn1_str_gc(backend, data):
|
||
|
s = _encode_asn1_str(backend, data)
|
||
|
s = backend._ffi.gc(s, backend._lib.ASN1_OCTET_STRING_free)
|
||
|
return s
|
||
|
|
||
|
|
||
|
def _encode_inhibit_any_policy(backend, inhibit_any_policy):
|
||
|
return _encode_asn1_int_gc(backend, inhibit_any_policy.skip_certs)
|
||
|
|
||
|
|
||
|
def _encode_name(backend, name):
|
||
|
"""
|
||
|
The X509_NAME created will not be gc'd. Use _encode_name_gc if needed.
|
||
|
"""
|
||
|
subject = backend._lib.X509_NAME_new()
|
||
|
for rdn in name.rdns:
|
||
|
set_flag = 0 # indicate whether to add to last RDN or create new RDN
|
||
|
for attribute in rdn:
|
||
|
name_entry = _encode_name_entry(backend, attribute)
|
||
|
# X509_NAME_add_entry dups the object so we need to gc this copy
|
||
|
name_entry = backend._ffi.gc(
|
||
|
name_entry, backend._lib.X509_NAME_ENTRY_free
|
||
|
)
|
||
|
res = backend._lib.X509_NAME_add_entry(
|
||
|
subject, name_entry, -1, set_flag)
|
||
|
backend.openssl_assert(res == 1)
|
||
|
set_flag = -1
|
||
|
return subject
|
||
|
|
||
|
|
||
|
def _encode_name_gc(backend, attributes):
|
||
|
subject = _encode_name(backend, attributes)
|
||
|
subject = backend._ffi.gc(subject, backend._lib.X509_NAME_free)
|
||
|
return subject
|
||
|
|
||
|
|
||
|
def _encode_sk_name_entry(backend, attributes):
|
||
|
"""
|
||
|
The sk_X509_NAME_ENTRY created will not be gc'd.
|
||
|
"""
|
||
|
stack = backend._lib.sk_X509_NAME_ENTRY_new_null()
|
||
|
for attribute in attributes:
|
||
|
name_entry = _encode_name_entry(backend, attribute)
|
||
|
res = backend._lib.sk_X509_NAME_ENTRY_push(stack, name_entry)
|
||
|
backend.openssl_assert(res >= 1)
|
||
|
return stack
|
||
|
|
||
|
|
||
|
def _encode_name_entry(backend, attribute):
|
||
|
if attribute._type is _ASN1Type.BMPString:
|
||
|
value = attribute.value.encode('utf_16_be')
|
||
|
else:
|
||
|
value = attribute.value.encode('utf8')
|
||
|
|
||
|
obj = _txt2obj_gc(backend, attribute.oid.dotted_string)
|
||
|
|
||
|
name_entry = backend._lib.X509_NAME_ENTRY_create_by_OBJ(
|
||
|
backend._ffi.NULL, obj, attribute._type.value, value, len(value)
|
||
|
)
|
||
|
return name_entry
|
||
|
|
||
|
|
||
|
def _encode_crl_number_delta_crl_indicator(backend, ext):
|
||
|
return _encode_asn1_int_gc(backend, ext.crl_number)
|
||
|
|
||
|
|
||
|
def _encode_issuing_dist_point(backend, ext):
|
||
|
idp = backend._lib.ISSUING_DIST_POINT_new()
|
||
|
backend.openssl_assert(idp != backend._ffi.NULL)
|
||
|
idp = backend._ffi.gc(idp, backend._lib.ISSUING_DIST_POINT_free)
|
||
|
idp.onlyuser = 255 if ext.only_contains_user_certs else 0
|
||
|
idp.onlyCA = 255 if ext.only_contains_ca_certs else 0
|
||
|
idp.indirectCRL = 255 if ext.indirect_crl else 0
|
||
|
idp.onlyattr = 255 if ext.only_contains_attribute_certs else 0
|
||
|
if ext.only_some_reasons:
|
||
|
idp.onlysomereasons = _encode_reasonflags(
|
||
|
backend, ext.only_some_reasons
|
||
|
)
|
||
|
|
||
|
if ext.full_name:
|
||
|
idp.distpoint = _encode_full_name(backend, ext.full_name)
|
||
|
|
||
|
if ext.relative_name:
|
||
|
idp.distpoint = _encode_relative_name(backend, ext.relative_name)
|
||
|
|
||
|
return idp
|
||
|
|
||
|
|
||
|
def _encode_crl_reason(backend, crl_reason):
|
||
|
asn1enum = backend._lib.ASN1_ENUMERATED_new()
|
||
|
backend.openssl_assert(asn1enum != backend._ffi.NULL)
|
||
|
asn1enum = backend._ffi.gc(asn1enum, backend._lib.ASN1_ENUMERATED_free)
|
||
|
res = backend._lib.ASN1_ENUMERATED_set(
|
||
|
asn1enum, _CRL_ENTRY_REASON_ENUM_TO_CODE[crl_reason.reason]
|
||
|
)
|
||
|
backend.openssl_assert(res == 1)
|
||
|
|
||
|
return asn1enum
|
||
|
|
||
|
|
||
|
def _encode_invalidity_date(backend, invalidity_date):
|
||
|
time = backend._lib.ASN1_GENERALIZEDTIME_set(
|
||
|
backend._ffi.NULL, calendar.timegm(
|
||
|
invalidity_date.invalidity_date.timetuple()
|
||
|
)
|
||
|
)
|
||
|
backend.openssl_assert(time != backend._ffi.NULL)
|
||
|
time = backend._ffi.gc(time, backend._lib.ASN1_GENERALIZEDTIME_free)
|
||
|
|
||
|
return time
|
||
|
|
||
|
|
||
|
def _encode_certificate_policies(backend, certificate_policies):
|
||
|
cp = backend._lib.sk_POLICYINFO_new_null()
|
||
|
backend.openssl_assert(cp != backend._ffi.NULL)
|
||
|
cp = backend._ffi.gc(cp, backend._lib.sk_POLICYINFO_free)
|
||
|
for policy_info in certificate_policies:
|
||
|
pi = backend._lib.POLICYINFO_new()
|
||
|
backend.openssl_assert(pi != backend._ffi.NULL)
|
||
|
res = backend._lib.sk_POLICYINFO_push(cp, pi)
|
||
|
backend.openssl_assert(res >= 1)
|
||
|
oid = _txt2obj(backend, policy_info.policy_identifier.dotted_string)
|
||
|
pi.policyid = oid
|
||
|
if policy_info.policy_qualifiers:
|
||
|
pqis = backend._lib.sk_POLICYQUALINFO_new_null()
|
||
|
backend.openssl_assert(pqis != backend._ffi.NULL)
|
||
|
for qualifier in policy_info.policy_qualifiers:
|
||
|
pqi = backend._lib.POLICYQUALINFO_new()
|
||
|
backend.openssl_assert(pqi != backend._ffi.NULL)
|
||
|
res = backend._lib.sk_POLICYQUALINFO_push(pqis, pqi)
|
||
|
backend.openssl_assert(res >= 1)
|
||
|
if isinstance(qualifier, six.text_type):
|
||
|
pqi.pqualid = _txt2obj(
|
||
|
backend, x509.OID_CPS_QUALIFIER.dotted_string
|
||
|
)
|
||
|
pqi.d.cpsuri = _encode_asn1_str(
|
||
|
backend,
|
||
|
qualifier.encode("ascii"),
|
||
|
)
|
||
|
else:
|
||
|
assert isinstance(qualifier, x509.UserNotice)
|
||
|
pqi.pqualid = _txt2obj(
|
||
|
backend, x509.OID_CPS_USER_NOTICE.dotted_string
|
||
|
)
|
||
|
un = backend._lib.USERNOTICE_new()
|
||
|
backend.openssl_assert(un != backend._ffi.NULL)
|
||
|
pqi.d.usernotice = un
|
||
|
if qualifier.explicit_text:
|
||
|
un.exptext = _encode_asn1_utf8_str(
|
||
|
backend, qualifier.explicit_text
|
||
|
)
|
||
|
|
||
|
un.noticeref = _encode_notice_reference(
|
||
|
backend, qualifier.notice_reference
|
||
|
)
|
||
|
|
||
|
pi.qualifiers = pqis
|
||
|
|
||
|
return cp
|
||
|
|
||
|
|
||
|
def _encode_notice_reference(backend, notice):
|
||
|
if notice is None:
|
||
|
return backend._ffi.NULL
|
||
|
else:
|
||
|
nr = backend._lib.NOTICEREF_new()
|
||
|
backend.openssl_assert(nr != backend._ffi.NULL)
|
||
|
# organization is a required field
|
||
|
nr.organization = _encode_asn1_utf8_str(backend, notice.organization)
|
||
|
|
||
|
notice_stack = backend._lib.sk_ASN1_INTEGER_new_null()
|
||
|
nr.noticenos = notice_stack
|
||
|
for number in notice.notice_numbers:
|
||
|
num = _encode_asn1_int(backend, number)
|
||
|
res = backend._lib.sk_ASN1_INTEGER_push(notice_stack, num)
|
||
|
backend.openssl_assert(res >= 1)
|
||
|
|
||
|
return nr
|
||
|
|
||
|
|
||
|
def _txt2obj(backend, name):
|
||
|
"""
|
||
|
Converts a Python string with an ASN.1 object ID in dotted form to a
|
||
|
ASN1_OBJECT.
|
||
|
"""
|
||
|
name = name.encode('ascii')
|
||
|
obj = backend._lib.OBJ_txt2obj(name, 1)
|
||
|
backend.openssl_assert(obj != backend._ffi.NULL)
|
||
|
return obj
|
||
|
|
||
|
|
||
|
def _txt2obj_gc(backend, name):
|
||
|
obj = _txt2obj(backend, name)
|
||
|
obj = backend._ffi.gc(obj, backend._lib.ASN1_OBJECT_free)
|
||
|
return obj
|
||
|
|
||
|
|
||
|
def _encode_ocsp_nocheck(backend, ext):
|
||
|
# Doesn't need to be GC'd
|
||
|
return backend._lib.ASN1_NULL_new()
|
||
|
|
||
|
|
||
|
def _encode_key_usage(backend, key_usage):
|
||
|
set_bit = backend._lib.ASN1_BIT_STRING_set_bit
|
||
|
ku = backend._lib.ASN1_BIT_STRING_new()
|
||
|
ku = backend._ffi.gc(ku, backend._lib.ASN1_BIT_STRING_free)
|
||
|
res = set_bit(ku, 0, key_usage.digital_signature)
|
||
|
backend.openssl_assert(res == 1)
|
||
|
res = set_bit(ku, 1, key_usage.content_commitment)
|
||
|
backend.openssl_assert(res == 1)
|
||
|
res = set_bit(ku, 2, key_usage.key_encipherment)
|
||
|
backend.openssl_assert(res == 1)
|
||
|
res = set_bit(ku, 3, key_usage.data_encipherment)
|
||
|
backend.openssl_assert(res == 1)
|
||
|
res = set_bit(ku, 4, key_usage.key_agreement)
|
||
|
backend.openssl_assert(res == 1)
|
||
|
res = set_bit(ku, 5, key_usage.key_cert_sign)
|
||
|
backend.openssl_assert(res == 1)
|
||
|
res = set_bit(ku, 6, key_usage.crl_sign)
|
||
|
backend.openssl_assert(res == 1)
|
||
|
if key_usage.key_agreement:
|
||
|
res = set_bit(ku, 7, key_usage.encipher_only)
|
||
|
backend.openssl_assert(res == 1)
|
||
|
res = set_bit(ku, 8, key_usage.decipher_only)
|
||
|
backend.openssl_assert(res == 1)
|
||
|
else:
|
||
|
res = set_bit(ku, 7, 0)
|
||
|
backend.openssl_assert(res == 1)
|
||
|
res = set_bit(ku, 8, 0)
|
||
|
backend.openssl_assert(res == 1)
|
||
|
|
||
|
return ku
|
||
|
|
||
|
|
||
|
def _encode_authority_key_identifier(backend, authority_keyid):
|
||
|
akid = backend._lib.AUTHORITY_KEYID_new()
|
||
|
backend.openssl_assert(akid != backend._ffi.NULL)
|
||
|
akid = backend._ffi.gc(akid, backend._lib.AUTHORITY_KEYID_free)
|
||
|
if authority_keyid.key_identifier is not None:
|
||
|
akid.keyid = _encode_asn1_str(
|
||
|
backend,
|
||
|
authority_keyid.key_identifier,
|
||
|
)
|
||
|
|
||
|
if authority_keyid.authority_cert_issuer is not None:
|
||
|
akid.issuer = _encode_general_names(
|
||
|
backend, authority_keyid.authority_cert_issuer
|
||
|
)
|
||
|
|
||
|
if authority_keyid.authority_cert_serial_number is not None:
|
||
|
akid.serial = _encode_asn1_int(
|
||
|
backend, authority_keyid.authority_cert_serial_number
|
||
|
)
|
||
|
|
||
|
return akid
|
||
|
|
||
|
|
||
|
def _encode_basic_constraints(backend, basic_constraints):
|
||
|
constraints = backend._lib.BASIC_CONSTRAINTS_new()
|
||
|
constraints = backend._ffi.gc(
|
||
|
constraints, backend._lib.BASIC_CONSTRAINTS_free
|
||
|
)
|
||
|
constraints.ca = 255 if basic_constraints.ca else 0
|
||
|
if basic_constraints.ca and basic_constraints.path_length is not None:
|
||
|
constraints.pathlen = _encode_asn1_int(
|
||
|
backend, basic_constraints.path_length
|
||
|
)
|
||
|
|
||
|
return constraints
|
||
|
|
||
|
|
||
|
def _encode_authority_information_access(backend, authority_info_access):
|
||
|
aia = backend._lib.sk_ACCESS_DESCRIPTION_new_null()
|
||
|
backend.openssl_assert(aia != backend._ffi.NULL)
|
||
|
aia = backend._ffi.gc(
|
||
|
aia, backend._lib.sk_ACCESS_DESCRIPTION_free
|
||
|
)
|
||
|
for access_description in authority_info_access:
|
||
|
ad = backend._lib.ACCESS_DESCRIPTION_new()
|
||
|
method = _txt2obj(
|
||
|
backend, access_description.access_method.dotted_string
|
||
|
)
|
||
|
gn = _encode_general_name(backend, access_description.access_location)
|
||
|
ad.method = method
|
||
|
ad.location = gn
|
||
|
res = backend._lib.sk_ACCESS_DESCRIPTION_push(aia, ad)
|
||
|
backend.openssl_assert(res >= 1)
|
||
|
|
||
|
return aia
|
||
|
|
||
|
|
||
|
def _encode_general_names(backend, names):
|
||
|
general_names = backend._lib.GENERAL_NAMES_new()
|
||
|
backend.openssl_assert(general_names != backend._ffi.NULL)
|
||
|
for name in names:
|
||
|
gn = _encode_general_name(backend, name)
|
||
|
res = backend._lib.sk_GENERAL_NAME_push(general_names, gn)
|
||
|
backend.openssl_assert(res != 0)
|
||
|
|
||
|
return general_names
|
||
|
|
||
|
|
||
|
def _encode_alt_name(backend, san):
|
||
|
general_names = _encode_general_names(backend, san)
|
||
|
general_names = backend._ffi.gc(
|
||
|
general_names, backend._lib.GENERAL_NAMES_free
|
||
|
)
|
||
|
return general_names
|
||
|
|
||
|
|
||
|
def _encode_subject_key_identifier(backend, ski):
|
||
|
return _encode_asn1_str_gc(backend, ski.digest)
|
||
|
|
||
|
|
||
|
def _encode_general_name(backend, name):
|
||
|
if isinstance(name, x509.DNSName):
|
||
|
gn = backend._lib.GENERAL_NAME_new()
|
||
|
backend.openssl_assert(gn != backend._ffi.NULL)
|
||
|
gn.type = backend._lib.GEN_DNS
|
||
|
|
||
|
ia5 = backend._lib.ASN1_IA5STRING_new()
|
||
|
backend.openssl_assert(ia5 != backend._ffi.NULL)
|
||
|
# ia5strings are supposed to be ITU T.50 but to allow round-tripping
|
||
|
# of broken certs that encode utf8 we'll encode utf8 here too.
|
||
|
value = name.value.encode("utf8")
|
||
|
|
||
|
res = backend._lib.ASN1_STRING_set(ia5, value, len(value))
|
||
|
backend.openssl_assert(res == 1)
|
||
|
gn.d.dNSName = ia5
|
||
|
elif isinstance(name, x509.RegisteredID):
|
||
|
gn = backend._lib.GENERAL_NAME_new()
|
||
|
backend.openssl_assert(gn != backend._ffi.NULL)
|
||
|
gn.type = backend._lib.GEN_RID
|
||
|
obj = backend._lib.OBJ_txt2obj(
|
||
|
name.value.dotted_string.encode('ascii'), 1
|
||
|
)
|
||
|
backend.openssl_assert(obj != backend._ffi.NULL)
|
||
|
gn.d.registeredID = obj
|
||
|
elif isinstance(name, x509.DirectoryName):
|
||
|
gn = backend._lib.GENERAL_NAME_new()
|
||
|
backend.openssl_assert(gn != backend._ffi.NULL)
|
||
|
dir_name = _encode_name(backend, name.value)
|
||
|
gn.type = backend._lib.GEN_DIRNAME
|
||
|
gn.d.directoryName = dir_name
|
||
|
elif isinstance(name, x509.IPAddress):
|
||
|
gn = backend._lib.GENERAL_NAME_new()
|
||
|
backend.openssl_assert(gn != backend._ffi.NULL)
|
||
|
if isinstance(name.value, ipaddress.IPv4Network):
|
||
|
packed = (
|
||
|
name.value.network_address.packed +
|
||
|
utils.int_to_bytes(((1 << 32) - name.value.num_addresses), 4)
|
||
|
)
|
||
|
elif isinstance(name.value, ipaddress.IPv6Network):
|
||
|
packed = (
|
||
|
name.value.network_address.packed +
|
||
|
utils.int_to_bytes((1 << 128) - name.value.num_addresses, 16)
|
||
|
)
|
||
|
else:
|
||
|
packed = name.value.packed
|
||
|
ipaddr = _encode_asn1_str(backend, packed)
|
||
|
gn.type = backend._lib.GEN_IPADD
|
||
|
gn.d.iPAddress = ipaddr
|
||
|
elif isinstance(name, x509.OtherName):
|
||
|
gn = backend._lib.GENERAL_NAME_new()
|
||
|
backend.openssl_assert(gn != backend._ffi.NULL)
|
||
|
other_name = backend._lib.OTHERNAME_new()
|
||
|
backend.openssl_assert(other_name != backend._ffi.NULL)
|
||
|
|
||
|
type_id = backend._lib.OBJ_txt2obj(
|
||
|
name.type_id.dotted_string.encode('ascii'), 1
|
||
|
)
|
||
|
backend.openssl_assert(type_id != backend._ffi.NULL)
|
||
|
data = backend._ffi.new("unsigned char[]", name.value)
|
||
|
data_ptr_ptr = backend._ffi.new("unsigned char **")
|
||
|
data_ptr_ptr[0] = data
|
||
|
value = backend._lib.d2i_ASN1_TYPE(
|
||
|
backend._ffi.NULL, data_ptr_ptr, len(name.value)
|
||
|
)
|
||
|
if value == backend._ffi.NULL:
|
||
|
backend._consume_errors()
|
||
|
raise ValueError("Invalid ASN.1 data")
|
||
|
other_name.type_id = type_id
|
||
|
other_name.value = value
|
||
|
gn.type = backend._lib.GEN_OTHERNAME
|
||
|
gn.d.otherName = other_name
|
||
|
elif isinstance(name, x509.RFC822Name):
|
||
|
gn = backend._lib.GENERAL_NAME_new()
|
||
|
backend.openssl_assert(gn != backend._ffi.NULL)
|
||
|
# ia5strings are supposed to be ITU T.50 but to allow round-tripping
|
||
|
# of broken certs that encode utf8 we'll encode utf8 here too.
|
||
|
data = name.value.encode("utf8")
|
||
|
asn1_str = _encode_asn1_str(backend, data)
|
||
|
gn.type = backend._lib.GEN_EMAIL
|
||
|
gn.d.rfc822Name = asn1_str
|
||
|
elif isinstance(name, x509.UniformResourceIdentifier):
|
||
|
gn = backend._lib.GENERAL_NAME_new()
|
||
|
backend.openssl_assert(gn != backend._ffi.NULL)
|
||
|
# ia5strings are supposed to be ITU T.50 but to allow round-tripping
|
||
|
# of broken certs that encode utf8 we'll encode utf8 here too.
|
||
|
data = name.value.encode("utf8")
|
||
|
asn1_str = _encode_asn1_str(backend, data)
|
||
|
gn.type = backend._lib.GEN_URI
|
||
|
gn.d.uniformResourceIdentifier = asn1_str
|
||
|
else:
|
||
|
raise ValueError(
|
||
|
"{} is an unknown GeneralName type".format(name)
|
||
|
)
|
||
|
|
||
|
return gn
|
||
|
|
||
|
|
||
|
def _encode_extended_key_usage(backend, extended_key_usage):
|
||
|
eku = backend._lib.sk_ASN1_OBJECT_new_null()
|
||
|
eku = backend._ffi.gc(eku, backend._lib.sk_ASN1_OBJECT_free)
|
||
|
for oid in extended_key_usage:
|
||
|
obj = _txt2obj(backend, oid.dotted_string)
|
||
|
res = backend._lib.sk_ASN1_OBJECT_push(eku, obj)
|
||
|
backend.openssl_assert(res >= 1)
|
||
|
|
||
|
return eku
|
||
|
|
||
|
|
||
|
_CRLREASONFLAGS = {
|
||
|
x509.ReasonFlags.key_compromise: 1,
|
||
|
x509.ReasonFlags.ca_compromise: 2,
|
||
|
x509.ReasonFlags.affiliation_changed: 3,
|
||
|
x509.ReasonFlags.superseded: 4,
|
||
|
x509.ReasonFlags.cessation_of_operation: 5,
|
||
|
x509.ReasonFlags.certificate_hold: 6,
|
||
|
x509.ReasonFlags.privilege_withdrawn: 7,
|
||
|
x509.ReasonFlags.aa_compromise: 8,
|
||
|
}
|
||
|
|
||
|
|
||
|
def _encode_reasonflags(backend, reasons):
|
||
|
bitmask = backend._lib.ASN1_BIT_STRING_new()
|
||
|
backend.openssl_assert(bitmask != backend._ffi.NULL)
|
||
|
for reason in reasons:
|
||
|
res = backend._lib.ASN1_BIT_STRING_set_bit(
|
||
|
bitmask, _CRLREASONFLAGS[reason], 1
|
||
|
)
|
||
|
backend.openssl_assert(res == 1)
|
||
|
|
||
|
return bitmask
|
||
|
|
||
|
|
||
|
def _encode_full_name(backend, full_name):
|
||
|
dpn = backend._lib.DIST_POINT_NAME_new()
|
||
|
backend.openssl_assert(dpn != backend._ffi.NULL)
|
||
|
dpn.type = _DISTPOINT_TYPE_FULLNAME
|
||
|
dpn.name.fullname = _encode_general_names(backend, full_name)
|
||
|
return dpn
|
||
|
|
||
|
|
||
|
def _encode_relative_name(backend, relative_name):
|
||
|
dpn = backend._lib.DIST_POINT_NAME_new()
|
||
|
backend.openssl_assert(dpn != backend._ffi.NULL)
|
||
|
dpn.type = _DISTPOINT_TYPE_RELATIVENAME
|
||
|
dpn.name.relativename = _encode_sk_name_entry(backend, relative_name)
|
||
|
return dpn
|
||
|
|
||
|
|
||
|
def _encode_cdps_freshest_crl(backend, cdps):
|
||
|
cdp = backend._lib.sk_DIST_POINT_new_null()
|
||
|
cdp = backend._ffi.gc(cdp, backend._lib.sk_DIST_POINT_free)
|
||
|
for point in cdps:
|
||
|
dp = backend._lib.DIST_POINT_new()
|
||
|
backend.openssl_assert(dp != backend._ffi.NULL)
|
||
|
|
||
|
if point.reasons:
|
||
|
dp.reasons = _encode_reasonflags(backend, point.reasons)
|
||
|
|
||
|
if point.full_name:
|
||
|
dp.distpoint = _encode_full_name(backend, point.full_name)
|
||
|
|
||
|
if point.relative_name:
|
||
|
dp.distpoint = _encode_relative_name(backend, point.relative_name)
|
||
|
|
||
|
if point.crl_issuer:
|
||
|
dp.CRLissuer = _encode_general_names(backend, point.crl_issuer)
|
||
|
|
||
|
res = backend._lib.sk_DIST_POINT_push(cdp, dp)
|
||
|
backend.openssl_assert(res >= 1)
|
||
|
|
||
|
return cdp
|
||
|
|
||
|
|
||
|
def _encode_name_constraints(backend, name_constraints):
|
||
|
nc = backend._lib.NAME_CONSTRAINTS_new()
|
||
|
backend.openssl_assert(nc != backend._ffi.NULL)
|
||
|
nc = backend._ffi.gc(nc, backend._lib.NAME_CONSTRAINTS_free)
|
||
|
permitted = _encode_general_subtree(
|
||
|
backend, name_constraints.permitted_subtrees
|
||
|
)
|
||
|
nc.permittedSubtrees = permitted
|
||
|
excluded = _encode_general_subtree(
|
||
|
backend, name_constraints.excluded_subtrees
|
||
|
)
|
||
|
nc.excludedSubtrees = excluded
|
||
|
|
||
|
return nc
|
||
|
|
||
|
|
||
|
def _encode_policy_constraints(backend, policy_constraints):
|
||
|
pc = backend._lib.POLICY_CONSTRAINTS_new()
|
||
|
backend.openssl_assert(pc != backend._ffi.NULL)
|
||
|
pc = backend._ffi.gc(pc, backend._lib.POLICY_CONSTRAINTS_free)
|
||
|
if policy_constraints.require_explicit_policy is not None:
|
||
|
pc.requireExplicitPolicy = _encode_asn1_int(
|
||
|
backend, policy_constraints.require_explicit_policy
|
||
|
)
|
||
|
|
||
|
if policy_constraints.inhibit_policy_mapping is not None:
|
||
|
pc.inhibitPolicyMapping = _encode_asn1_int(
|
||
|
backend, policy_constraints.inhibit_policy_mapping
|
||
|
)
|
||
|
|
||
|
return pc
|
||
|
|
||
|
|
||
|
def _encode_general_subtree(backend, subtrees):
|
||
|
if subtrees is None:
|
||
|
return backend._ffi.NULL
|
||
|
else:
|
||
|
general_subtrees = backend._lib.sk_GENERAL_SUBTREE_new_null()
|
||
|
for name in subtrees:
|
||
|
gs = backend._lib.GENERAL_SUBTREE_new()
|
||
|
gs.base = _encode_general_name(backend, name)
|
||
|
res = backend._lib.sk_GENERAL_SUBTREE_push(general_subtrees, gs)
|
||
|
assert res >= 1
|
||
|
|
||
|
return general_subtrees
|
||
|
|
||
|
|
||
|
def _encode_nonce(backend, nonce):
|
||
|
return _encode_asn1_str_gc(backend, nonce.nonce)
|
||
|
|
||
|
|
||
|
_EXTENSION_ENCODE_HANDLERS = {
|
||
|
ExtensionOID.BASIC_CONSTRAINTS: _encode_basic_constraints,
|
||
|
ExtensionOID.SUBJECT_KEY_IDENTIFIER: _encode_subject_key_identifier,
|
||
|
ExtensionOID.KEY_USAGE: _encode_key_usage,
|
||
|
ExtensionOID.SUBJECT_ALTERNATIVE_NAME: _encode_alt_name,
|
||
|
ExtensionOID.ISSUER_ALTERNATIVE_NAME: _encode_alt_name,
|
||
|
ExtensionOID.EXTENDED_KEY_USAGE: _encode_extended_key_usage,
|
||
|
ExtensionOID.AUTHORITY_KEY_IDENTIFIER: _encode_authority_key_identifier,
|
||
|
ExtensionOID.CERTIFICATE_POLICIES: _encode_certificate_policies,
|
||
|
ExtensionOID.AUTHORITY_INFORMATION_ACCESS: (
|
||
|
_encode_authority_information_access
|
||
|
),
|
||
|
ExtensionOID.CRL_DISTRIBUTION_POINTS: _encode_cdps_freshest_crl,
|
||
|
ExtensionOID.FRESHEST_CRL: _encode_cdps_freshest_crl,
|
||
|
ExtensionOID.INHIBIT_ANY_POLICY: _encode_inhibit_any_policy,
|
||
|
ExtensionOID.OCSP_NO_CHECK: _encode_ocsp_nocheck,
|
||
|
ExtensionOID.NAME_CONSTRAINTS: _encode_name_constraints,
|
||
|
ExtensionOID.POLICY_CONSTRAINTS: _encode_policy_constraints,
|
||
|
}
|
||
|
|
||
|
_CRL_EXTENSION_ENCODE_HANDLERS = {
|
||
|
ExtensionOID.ISSUER_ALTERNATIVE_NAME: _encode_alt_name,
|
||
|
ExtensionOID.AUTHORITY_KEY_IDENTIFIER: _encode_authority_key_identifier,
|
||
|
ExtensionOID.AUTHORITY_INFORMATION_ACCESS: (
|
||
|
_encode_authority_information_access
|
||
|
),
|
||
|
ExtensionOID.CRL_NUMBER: _encode_crl_number_delta_crl_indicator,
|
||
|
ExtensionOID.DELTA_CRL_INDICATOR: _encode_crl_number_delta_crl_indicator,
|
||
|
ExtensionOID.ISSUING_DISTRIBUTION_POINT: _encode_issuing_dist_point,
|
||
|
}
|
||
|
|
||
|
_CRL_ENTRY_EXTENSION_ENCODE_HANDLERS = {
|
||
|
CRLEntryExtensionOID.CERTIFICATE_ISSUER: _encode_alt_name,
|
||
|
CRLEntryExtensionOID.CRL_REASON: _encode_crl_reason,
|
||
|
CRLEntryExtensionOID.INVALIDITY_DATE: _encode_invalidity_date,
|
||
|
}
|
||
|
|
||
|
_OCSP_REQUEST_EXTENSION_ENCODE_HANDLERS = {
|
||
|
OCSPExtensionOID.NONCE: _encode_nonce,
|
||
|
}
|
||
|
|
||
|
_OCSP_BASICRESP_EXTENSION_ENCODE_HANDLERS = {
|
||
|
OCSPExtensionOID.NONCE: _encode_nonce,
|
||
|
}
|