Implement Ed25519, Ed448, X25519 and X448 support (cryptography backend). (#54947)
This commit is contained in:
parent
7a16703dff
commit
221da3e8b1
3 changed files with 82 additions and 17 deletions
|
@ -19,9 +19,9 @@ short_description: Generate OpenSSL private keys
|
|||
description:
|
||||
- This module allows one to (re)generate OpenSSL private keys.
|
||||
- One can generate L(RSA,https://en.wikipedia.org/wiki/RSA_(cryptosystem)),
|
||||
L(DSA,https://en.wikipedia.org/wiki/Digital_Signature_Algorithm) or
|
||||
L(ECC,https://en.wikipedia.org/wiki/Elliptic-curve_cryptography)
|
||||
private keys.
|
||||
L(DSA,https://en.wikipedia.org/wiki/Digital_Signature_Algorithm),
|
||||
L(ECC,https://en.wikipedia.org/wiki/Elliptic-curve_cryptography) or
|
||||
L(EdDSA,https://en.wikipedia.org/wiki/EdDSA) private keys.
|
||||
- Keys are generated in PEM format.
|
||||
- "Please note that the module regenerates private keys if they don't match
|
||||
the module's options. In particular, if you provide another passphrase
|
||||
|
@ -52,12 +52,13 @@ options:
|
|||
type:
|
||||
description:
|
||||
- The algorithm used to generate the TLS/SSL private key.
|
||||
- Note that C(ECC) requires the C(cryptography) backend.
|
||||
- Depending on the curve, you need a newer version of the cryptography backend.
|
||||
- Note that C(ECC), C(X25519), C(X448), C(Ed25519) and C(Ed448) require the C(cryptography) backend.
|
||||
C(X25519) needs cryptography 2.5 or newer, while C(X448), C(Ed25519) and C(Ed448) require
|
||||
cryptography 2.6 or newer. For C(ECC), the minimal cryptography version required depends on the
|
||||
I(curve) option.
|
||||
type: str
|
||||
default: RSA
|
||||
#choices: [ DSA, ECC, RSA, X448, X25519, Ed448, Ed25519 ]
|
||||
choices: [ DSA, ECC, RSA ]
|
||||
choices: [ DSA, ECC, Ed25519, Ed448, RSA, X25519, X448 ]
|
||||
curve:
|
||||
description:
|
||||
- Note that not all curves are supported by all versions of C(cryptography).
|
||||
|
@ -239,8 +240,14 @@ else:
|
|||
try:
|
||||
import cryptography.hazmat.primitives.asymmetric.x25519
|
||||
CRYPTOGRAPHY_HAS_X25519 = True
|
||||
try:
|
||||
cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey.private_bytes
|
||||
CRYPTOGRAPHY_HAS_X25519_FULL = True
|
||||
except AttributeError:
|
||||
CRYPTOGRAPHY_HAS_X25519_FULL = False
|
||||
except ImportError:
|
||||
CRYPTOGRAPHY_HAS_X25519 = False
|
||||
CRYPTOGRAPHY_HAS_X25519_FULL = False
|
||||
try:
|
||||
import cryptography.hazmat.primitives.asymmetric.x448
|
||||
CRYPTOGRAPHY_HAS_X448 = True
|
||||
|
@ -467,6 +474,8 @@ class PrivateKeyCryptography(PrivateKeyBase):
|
|||
self.curve = module.params['curve']
|
||||
if not CRYPTOGRAPHY_HAS_X25519 and self.type == 'X25519':
|
||||
self.module.fail_json(msg='Your cryptography version does not support X25519')
|
||||
if not CRYPTOGRAPHY_HAS_X25519_FULL and self.type == 'X25519':
|
||||
self.module.fail_json(msg='Your cryptography version does not support X25519 serialization')
|
||||
if not CRYPTOGRAPHY_HAS_X448 and self.type == 'X448':
|
||||
self.module.fail_json(msg='Your cryptography version does not support X448')
|
||||
if not CRYPTOGRAPHY_HAS_ED25519 and self.type == 'Ed25519':
|
||||
|
@ -475,6 +484,7 @@ class PrivateKeyCryptography(PrivateKeyBase):
|
|||
self.module.fail_json(msg='Your cryptography version does not support Ed448')
|
||||
|
||||
def _generate_private_key_data(self):
|
||||
format = cryptography.hazmat.primitives.serialization.PrivateFormat.TraditionalOpenSSL
|
||||
try:
|
||||
if self.type == 'RSA':
|
||||
self.privatekey = cryptography.hazmat.primitives.asymmetric.rsa.generate_private_key(
|
||||
|
@ -487,14 +497,18 @@ class PrivateKeyCryptography(PrivateKeyBase):
|
|||
key_size=self.size,
|
||||
backend=self.cryptography_backend
|
||||
)
|
||||
if CRYPTOGRAPHY_HAS_X25519 and self.type == 'X25519':
|
||||
if CRYPTOGRAPHY_HAS_X25519_FULL and self.type == 'X25519':
|
||||
self.privatekey = cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey.generate()
|
||||
format = cryptography.hazmat.primitives.serialization.PrivateFormat.PKCS8
|
||||
if CRYPTOGRAPHY_HAS_X448 and self.type == 'X448':
|
||||
self.privatekey = cryptography.hazmat.primitives.asymmetric.x448.X448PrivateKey.generate()
|
||||
format = cryptography.hazmat.primitives.serialization.PrivateFormat.PKCS8
|
||||
if CRYPTOGRAPHY_HAS_ED25519 and self.type == 'Ed25519':
|
||||
self.privatekey = cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey.generate()
|
||||
format = cryptography.hazmat.primitives.serialization.PrivateFormat.PKCS8
|
||||
if CRYPTOGRAPHY_HAS_ED448 and self.type == 'Ed448':
|
||||
self.privatekey = cryptography.hazmat.primitives.asymmetric.ed448.Ed448PrivateKey.generate()
|
||||
format = cryptography.hazmat.primitives.serialization.PrivateFormat.PKCS8
|
||||
if self.type == 'ECC' and self.curve in self.curves:
|
||||
if self.curves[self.curve]['deprecated']:
|
||||
self.module.warn('Elliptic curves of type {0} should not be used for new keys!'.format(self.curve))
|
||||
|
@ -516,7 +530,7 @@ class PrivateKeyCryptography(PrivateKeyBase):
|
|||
# Serialize key
|
||||
return self.privatekey.private_bytes(
|
||||
encoding=cryptography.hazmat.primitives.serialization.Encoding.PEM,
|
||||
format=cryptography.hazmat.primitives.serialization.PrivateFormat.TraditionalOpenSSL,
|
||||
format=format,
|
||||
encryption_algorithm=encryption_algorithm
|
||||
)
|
||||
|
||||
|
@ -565,6 +579,10 @@ class PrivateKeyCryptography(PrivateKeyBase):
|
|||
return self.type == 'X25519'
|
||||
if CRYPTOGRAPHY_HAS_X448 and isinstance(privatekey, cryptography.hazmat.primitives.asymmetric.x448.X448PrivateKey):
|
||||
return self.type == 'X448'
|
||||
if CRYPTOGRAPHY_HAS_ED25519 and isinstance(privatekey, cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey):
|
||||
return self.type == 'Ed25519'
|
||||
if CRYPTOGRAPHY_HAS_ED448 and isinstance(privatekey, cryptography.hazmat.primitives.asymmetric.ed448.Ed448PrivateKey):
|
||||
return self.type == 'Ed448'
|
||||
if isinstance(privatekey, cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey):
|
||||
if self.type != 'ECC':
|
||||
return False
|
||||
|
@ -590,10 +608,7 @@ def main():
|
|||
state=dict(type='str', default='present', choices=['present', 'absent']),
|
||||
size=dict(type='int', default=4096),
|
||||
type=dict(type='str', default='RSA', choices=[
|
||||
'RSA', 'DSA', 'ECC',
|
||||
# FIXME: NO LONGER TRUE: x25519 is missing serialization functions: https://github.com/pyca/cryptography/issues/4386
|
||||
# FIXME: NO LONGER TRUE: x448 is also missing it: https://github.com/pyca/cryptography/pull/4580#issuecomment-437913340
|
||||
# 'X448', 'X25519', 'Ed448', 'Ed25519'
|
||||
'DSA', 'ECC', 'Ed25519', 'Ed448', 'RSA', 'X25519', 'X448'
|
||||
]),
|
||||
curve=dict(type='str', choices=[
|
||||
'secp384r1', 'secp521r1', 'secp224r1', 'secp192r1', 'secp256k1',
|
||||
|
|
|
@ -55,10 +55,6 @@
|
|||
when: select_crypto_backend == 'pyopenssl'
|
||||
- set_fact:
|
||||
ecc_types:
|
||||
# - curve: X448
|
||||
# min_cryptography_version: "2.5"
|
||||
# - curve: X25519
|
||||
# min_cryptography_version: "2.0"
|
||||
- curve: secp384r1
|
||||
openssl_name: secp384r1
|
||||
min_cryptography_version: "0.5"
|
||||
|
@ -143,6 +139,41 @@
|
|||
label: "{{ item.curve }}"
|
||||
register: privatekey_ecc_idempotency
|
||||
|
||||
- block:
|
||||
- name: Test other type generation
|
||||
openssl_privatekey:
|
||||
path: '{{ output_dir }}/privatekey-{{ item.type }}.pem'
|
||||
type: "{{ item.type }}"
|
||||
select_crypto_backend: '{{ select_crypto_backend }}'
|
||||
when: cryptography_version.stdout is version(item.min_version, '>=')
|
||||
loop: "{{ types }}"
|
||||
loop_control:
|
||||
label: "{{ item.type }}"
|
||||
register: privatekey_t1_generate
|
||||
|
||||
- name: Test other type generation (idempotency)
|
||||
openssl_privatekey:
|
||||
path: '{{ output_dir }}/privatekey-{{ item.type }}.pem'
|
||||
type: "{{ item.type }}"
|
||||
select_crypto_backend: '{{ select_crypto_backend }}'
|
||||
when: cryptography_version.stdout is version(item.min_version, '>=')
|
||||
loop: "{{ types }}"
|
||||
loop_control:
|
||||
label: "{{ item.type }}"
|
||||
register: privatekey_t1_idempotency
|
||||
|
||||
when: select_crypto_backend == 'cryptography'
|
||||
vars:
|
||||
types:
|
||||
- type: X25519
|
||||
min_version: '2.5'
|
||||
- type: Ed25519
|
||||
min_version: '2.6'
|
||||
- type: Ed448
|
||||
min_version: '2.6'
|
||||
- type: X448
|
||||
min_version: '2.6'
|
||||
|
||||
- name: Generate privatekey with passphrase
|
||||
openssl_privatekey:
|
||||
path: '{{ output_dir }}/privatekeypw.pem'
|
||||
|
|
|
@ -105,6 +105,25 @@
|
|||
loop_control:
|
||||
label: "{{ item.item.curve }}"
|
||||
|
||||
- name: Validate other type generation (just check changed)
|
||||
assert:
|
||||
that:
|
||||
- item is changed
|
||||
loop: "{{ privatekey_t1_generate.results }}"
|
||||
when: "'skip_reason' not in item"
|
||||
loop_control:
|
||||
label: "{{ item.item.type }}"
|
||||
|
||||
|
||||
- name: Validate other type generation idempotency
|
||||
assert:
|
||||
that:
|
||||
- item is not changed
|
||||
loop: "{{ privatekey_t1_idempotency.results }}"
|
||||
when: "'skip_reason' not in item"
|
||||
loop_control:
|
||||
label: "{{ item.item.type }}"
|
||||
|
||||
- name: Validate passphrase changing
|
||||
assert:
|
||||
that:
|
||||
|
|
Loading…
Reference in a new issue