mirror of
https://github.com/ansible-collections/community.crypto.git
synced 2026-05-06 13:22:58 +00:00
Add conversion filters for serial numbers (#713)
* Refactoring. * Add parse_filter and to_filter plugins. * Mention filters when serial numbers are accepted or returned.
This commit is contained in:
@@ -13,7 +13,6 @@ import base64
|
||||
import binascii
|
||||
import datetime
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
|
||||
@@ -37,6 +36,11 @@ from ansible_collections.community.crypto.plugins.module_utils.acme.io import re
|
||||
|
||||
from ansible_collections.community.crypto.plugins.module_utils.acme.utils import nopad_b64
|
||||
|
||||
from ansible_collections.community.crypto.plugins.module_utils.crypto.math import (
|
||||
convert_int_to_bytes,
|
||||
convert_int_to_hex,
|
||||
)
|
||||
|
||||
from ansible_collections.community.crypto.plugins.module_utils.crypto.support import (
|
||||
parse_name_field,
|
||||
)
|
||||
@@ -78,40 +82,6 @@ else:
|
||||
CRYPTOGRAPHY_ERROR = traceback.format_exc()
|
||||
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
# Python 3 (and newer)
|
||||
def _count_bytes(n):
|
||||
return (n.bit_length() + 7) // 8 if n > 0 else 0
|
||||
|
||||
def _convert_int_to_bytes(count, no):
|
||||
return no.to_bytes(count, byteorder='big')
|
||||
|
||||
def _pad_hex(n, digits):
|
||||
res = hex(n)[2:]
|
||||
if len(res) < digits:
|
||||
res = '0' * (digits - len(res)) + res
|
||||
return res
|
||||
else:
|
||||
# Python 2
|
||||
def _count_bytes(n):
|
||||
if n <= 0:
|
||||
return 0
|
||||
h = '%x' % n
|
||||
return (len(h) + 1) // 2
|
||||
|
||||
def _convert_int_to_bytes(count, n):
|
||||
h = '%x' % n
|
||||
if len(h) > 2 * count:
|
||||
raise Exception('Number {1} needs more than {0} bytes!'.format(count, n))
|
||||
return ('0' * (2 * count - len(h)) + h).decode('hex')
|
||||
|
||||
def _pad_hex(n, digits):
|
||||
h = '%x' % n
|
||||
if len(h) < digits:
|
||||
h = '0' * (digits - len(h)) + h
|
||||
return h
|
||||
|
||||
|
||||
class CryptographyChainMatcher(ChainMatcher):
|
||||
@staticmethod
|
||||
def _parse_key_identifier(key_identifier, name, criterium_idx, module):
|
||||
@@ -223,8 +193,8 @@ class CryptographyBackend(CryptoBackend):
|
||||
'alg': 'RS256',
|
||||
'jwk': {
|
||||
"kty": "RSA",
|
||||
"e": nopad_b64(_convert_int_to_bytes(_count_bytes(pk.e), pk.e)),
|
||||
"n": nopad_b64(_convert_int_to_bytes(_count_bytes(pk.n), pk.n)),
|
||||
"e": nopad_b64(convert_int_to_bytes(pk.e)),
|
||||
"n": nopad_b64(convert_int_to_bytes(pk.n)),
|
||||
},
|
||||
'hash': 'sha256',
|
||||
}
|
||||
@@ -260,8 +230,8 @@ class CryptographyBackend(CryptoBackend):
|
||||
'jwk': {
|
||||
"kty": "EC",
|
||||
"crv": curve,
|
||||
"x": nopad_b64(_convert_int_to_bytes(num_bytes, pk.x)),
|
||||
"y": nopad_b64(_convert_int_to_bytes(num_bytes, pk.y)),
|
||||
"x": nopad_b64(convert_int_to_bytes(pk.x, count=num_bytes)),
|
||||
"y": nopad_b64(convert_int_to_bytes(pk.y, count=num_bytes)),
|
||||
},
|
||||
'hash': hashalg,
|
||||
'point_size': point_size,
|
||||
@@ -288,8 +258,8 @@ class CryptographyBackend(CryptoBackend):
|
||||
hashalg = cryptography.hazmat.primitives.hashes.SHA512
|
||||
ecdsa = cryptography.hazmat.primitives.asymmetric.ec.ECDSA(hashalg())
|
||||
r, s = cryptography.hazmat.primitives.asymmetric.utils.decode_dss_signature(key_data['key_obj'].sign(sign_payload, ecdsa))
|
||||
rr = _pad_hex(r, 2 * key_data['point_size'])
|
||||
ss = _pad_hex(s, 2 * key_data['point_size'])
|
||||
rr = convert_int_to_hex(r, 2 * key_data['point_size'])
|
||||
ss = convert_int_to_hex(s, 2 * key_data['point_size'])
|
||||
signature = binascii.unhexlify(rr) + binascii.unhexlify(ss)
|
||||
|
||||
return {
|
||||
|
||||
@@ -54,17 +54,93 @@ def quick_is_not_prime(n):
|
||||
python_version = (sys.version_info[0], sys.version_info[1])
|
||||
if python_version >= (2, 7) or python_version >= (3, 1):
|
||||
# Ansible still supports Python 2.6 on remote nodes
|
||||
|
||||
def count_bytes(no):
|
||||
"""
|
||||
Given an integer, compute the number of bytes necessary to store its absolute value.
|
||||
"""
|
||||
no = abs(no)
|
||||
if no == 0:
|
||||
return 0
|
||||
return (no.bit_length() + 7) // 8
|
||||
|
||||
def count_bits(no):
|
||||
"""
|
||||
Given an integer, compute the number of bits necessary to store its absolute value.
|
||||
"""
|
||||
no = abs(no)
|
||||
if no == 0:
|
||||
return 0
|
||||
return no.bit_length()
|
||||
else:
|
||||
# Slow, but works
|
||||
def count_bytes(no):
|
||||
"""
|
||||
Given an integer, compute the number of bytes necessary to store its absolute value.
|
||||
"""
|
||||
no = abs(no)
|
||||
count = 0
|
||||
while no > 0:
|
||||
no >>= 8
|
||||
count += 1
|
||||
return count
|
||||
|
||||
def count_bits(no):
|
||||
"""
|
||||
Given an integer, compute the number of bits necessary to store its absolute value.
|
||||
"""
|
||||
no = abs(no)
|
||||
count = 0
|
||||
while no > 0:
|
||||
no >>= 1
|
||||
count += 1
|
||||
return count
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
# Python 3 (and newer)
|
||||
def _convert_int_to_bytes(count, no):
|
||||
return no.to_bytes(count, byteorder='big')
|
||||
|
||||
def _to_hex(no):
|
||||
return hex(no)[2:]
|
||||
else:
|
||||
# Python 2
|
||||
def _convert_int_to_bytes(count, n):
|
||||
h = '%x' % n
|
||||
if len(h) > 2 * count:
|
||||
raise Exception('Number {1} needs more than {0} bytes!'.format(count, n))
|
||||
return ('0' * (2 * count - len(h)) + h).decode('hex')
|
||||
|
||||
def _to_hex(no):
|
||||
return '%x' % no
|
||||
|
||||
|
||||
def convert_int_to_bytes(no, count=None):
|
||||
"""
|
||||
Convert the absolute value of an integer to a byte string in network byte order.
|
||||
|
||||
If ``count`` is provided, it must be sufficiently large so that the integer's
|
||||
absolute value can be represented with these number of bytes. The resulting byte
|
||||
string will have length exactly ``count``.
|
||||
|
||||
The value zero will be converted to an empty byte string if ``count`` is provided.
|
||||
"""
|
||||
no = abs(no)
|
||||
if count is None:
|
||||
count = count_bytes(no)
|
||||
return _convert_int_to_bytes(count, no)
|
||||
|
||||
|
||||
def convert_int_to_hex(no, digits=None):
|
||||
"""
|
||||
Convert the absolute value of an integer to a string of hexadecimal digits.
|
||||
|
||||
If ``digits`` is provided, the string will be padded on the left with ``0``s so
|
||||
that the returned value has length ``digits``. If ``digits`` is not sufficient,
|
||||
the string will be longer.
|
||||
"""
|
||||
no = abs(no)
|
||||
value = _to_hex(no)
|
||||
if digits is not None and len(value) < digits:
|
||||
value = '0' * (digits - len(value)) + value
|
||||
return value
|
||||
|
||||
56
plugins/module_utils/serial.py
Normal file
56
plugins/module_utils/serial.py
Normal file
@@ -0,0 +1,56 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2024, Felix Fontein <felix@fontein.de>
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
|
||||
from ansible_collections.community.crypto.plugins.module_utils.crypto.math import (
|
||||
convert_int_to_hex,
|
||||
)
|
||||
|
||||
|
||||
def th(number):
|
||||
abs_number = abs(number)
|
||||
mod_10 = abs_number % 10
|
||||
mod_100 = abs_number % 100
|
||||
if mod_100 not in (11, 12, 13):
|
||||
if mod_10 == 1:
|
||||
return 'st'
|
||||
if mod_10 == 2:
|
||||
return 'nd'
|
||||
if mod_10 == 3:
|
||||
return 'rd'
|
||||
return 'th'
|
||||
|
||||
|
||||
def parse_serial(value):
|
||||
"""
|
||||
Given a colon-separated string of hexadecimal byte values, converts it to an integer.
|
||||
"""
|
||||
value = to_native(value)
|
||||
result = 0
|
||||
for i, part in enumerate(value.split(':')):
|
||||
try:
|
||||
part_value = int(part, 16)
|
||||
if part_value < 0 or part_value > 255:
|
||||
raise ValueError('the value is not in range [0, 255]')
|
||||
except ValueError as exc:
|
||||
raise ValueError("The {idx}{th} part {part!r} is not a hexadecimal number in range [0, 255]: {exc}".format(
|
||||
idx=i + 1, th=th(i + 1), part=part, exc=exc))
|
||||
result = (result << 8) | part_value
|
||||
return result
|
||||
|
||||
|
||||
def to_serial(value):
|
||||
"""
|
||||
Given an integer, converts its absolute value to a colon-separated string of hexadecimal byte values.
|
||||
"""
|
||||
value = convert_int_to_hex(value).upper()
|
||||
if len(value) % 2 != 0:
|
||||
value = '0' + value
|
||||
return ':'.join(value[i:i + 2] for i in range(0, len(value), 2))
|
||||
Reference in New Issue
Block a user