Implement Ed25519, Ed448, X25519 and X448 support (cryptography backend). (#54947)

This commit is contained in:
Felix Fontein 2019-04-08 10:30:05 +02:00 committed by Martin Krizek
parent 7a16703dff
commit 221da3e8b1
3 changed files with 82 additions and 17 deletions

View file

@ -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',

View file

@ -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'

View file

@ -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: