1079a7
From 895e99b6843c2fa2274acab824607c33c1a560a4 Mon Sep 17 00:00:00 2001
1079a7
From: Christian Heimes <cheimes@redhat.com>
1079a7
Date: Mon, 7 Oct 2019 14:13:03 +0200
1079a7
Subject: [PATCH] Support AES for KRA archival wrapping
1079a7
1079a7
The vault plugin has used TripleDES (des-ede3-cbc) as default wrapping
1079a7
algorithm since the plugin was introduced. Allow use of AES-128-CBC as
1079a7
alternative wrapping algorithm for transport of secrets.
1079a7
1079a7
Fixes: https://pagure.io/freeipa/issue/6524
1079a7
1079a7
Signed-off-by: Christian Heimes <cheimes@redhat.com>
1079a7
Reviewed-By: Christian Heimes <cheimes@redhat.com>
1079a7
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
1079a7
---
1079a7
 API.txt                    |   7 +-
1079a7
 VERSION.m4                 |   5 +-
1079a7
 ipaclient/plugins/vault.py | 155 +++++++++++++++++++++++++------------
1079a7
 ipalib/capabilities.py     |   4 +
1079a7
 ipalib/constants.py        |  12 +++
1079a7
 ipaserver/plugins/vault.py |  61 ++++++++++++---
1079a7
 6 files changed, 180 insertions(+), 64 deletions(-)
1079a7
1079a7
diff --git a/API.txt b/API.txt
1079a7
index 576fa7c51e31886b257ccf176aaf232c0f2ea5ee..f95f2c8457e39f2268386a8a2336952d3285e008 100644
1079a7
--- a/API.txt
1079a7
+++ b/API.txt
1079a7
@@ -6548,7 +6548,7 @@ output: Output('completed', type=[<type 'int'>])
1079a7
 output: Output('failed', type=[<type 'dict'>])
1079a7
 output: Entry('result')
1079a7
 command: vault_archive_internal/1
1079a7
-args: 1,9,3
1079a7
+args: 1,10,3
1079a7
 arg: Str('cn', cli_name='name')
1079a7
 option: Flag('all', autofill=True, cli_name='all', default=False)
1079a7
 option: Bytes('nonce')
1079a7
@@ -6559,6 +6559,7 @@ option: Flag('shared?', autofill=True, default=False)
1079a7
 option: Str('username?', cli_name='user')
1079a7
 option: Bytes('vault_data')
1079a7
 option: Str('version?')
1079a7
+option: StrEnum('wrapping_algo?', autofill=True, default=u'des-ede3-cbc', values=[u'des-ede3-cbc', u'aes-128-cbc'])
1079a7
 output: Entry('result')
1079a7
 output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
1079a7
 output: PrimaryKey('value')
1079a7
@@ -6649,7 +6650,7 @@ output: Output('completed', type=[<type 'int'>])
1079a7
 output: Output('failed', type=[<type 'dict'>])
1079a7
 output: Entry('result')
1079a7
 command: vault_retrieve_internal/1
1079a7
-args: 1,7,3
1079a7
+args: 1,8,3
1079a7
 arg: Str('cn', cli_name='name')
1079a7
 option: Flag('all', autofill=True, cli_name='all', default=False)
1079a7
 option: Flag('raw', autofill=True, cli_name='raw', default=False)
1079a7
@@ -6658,6 +6659,7 @@ option: Bytes('session_key')
1079a7
 option: Flag('shared?', autofill=True, default=False)
1079a7
 option: Str('username?', cli_name='user')
1079a7
 option: Str('version?')
1079a7
+option: StrEnum('wrapping_algo?', autofill=True, default=u'des-ede3-cbc', values=[u'des-ede3-cbc', u'aes-128-cbc'])
1079a7
 output: Entry('result')
1079a7
 output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
1079a7
 output: PrimaryKey('value')
1079a7
@@ -7327,6 +7329,7 @@ default: vaultcontainer_del/1
1079a7
 default: vaultcontainer_remove_owner/1
1079a7
 default: vaultcontainer_show/1
1079a7
 default: whoami/1
1079a7
+capability: vault_aes_keywrap 2.246
1079a7
 capability: messages 2.52
1079a7
 capability: optional_uid_params 2.54
1079a7
 capability: permissions2 2.69
1079a7
diff --git a/VERSION.m4 b/VERSION.m4
1079a7
index 70aaff4c9b9514a5937eae60074376e1a592464e..997ac35e74fa6f2a96da027ed3ce93cf809b62a7 100644
1079a7
--- a/VERSION.m4
1079a7
+++ b/VERSION.m4
1079a7
@@ -86,9 +86,8 @@ define(IPA_DATA_VERSION, 20100614120000)
1079a7
 #                                                      #
1079a7
 ########################################################
1079a7
 define(IPA_API_VERSION_MAJOR, 2)
1079a7
-# Last change: add enable_sid to config
1079a7
-define(IPA_API_VERSION_MINOR, 245)
1079a7
-
1079a7
+# Last change: Add wrapping algorithm to vault archive/retrieve
1079a7
+define(IPA_API_VERSION_MINOR, 246)
1079a7
 
1079a7
 ########################################################
1079a7
 # Following values are auto-generated from values above
1079a7
diff --git a/ipaclient/plugins/vault.py b/ipaclient/plugins/vault.py
1079a7
index d3a1d370efaccc7e5b0088bd3df341d76884d509..115171c7768d44251c17d0bcdac9c37b3a25db99 100644
1079a7
--- a/ipaclient/plugins/vault.py
1079a7
+++ b/ipaclient/plugins/vault.py
1079a7
@@ -25,11 +25,12 @@ import io
1079a7
 import json
1079a7
 import logging
1079a7
 import os
1079a7
+import ssl
1079a7
 import tempfile
1079a7
 
1079a7
 from cryptography.fernet import Fernet, InvalidToken
1079a7
 from cryptography.hazmat.backends import default_backend
1079a7
-from cryptography.hazmat.primitives import hashes, serialization
1079a7
+from cryptography.hazmat.primitives import hashes
1079a7
 from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
1079a7
 from cryptography.hazmat.primitives.asymmetric import padding
1079a7
 from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
1079a7
@@ -39,7 +40,7 @@ from cryptography.hazmat.primitives.serialization import (
1079a7
 
1079a7
 from ipaclient.frontend import MethodOverride
1079a7
 from ipalib import x509
1079a7
-from ipalib.constants import USER_CACHE_PATH
1079a7
+from ipalib import constants
1079a7
 from ipalib.frontend import Local, Method, Object
1079a7
 from ipalib.util import classproperty
1079a7
 from ipalib import api, errors
1079a7
@@ -546,42 +547,49 @@ class vault_mod(Local):
1079a7
         return response
1079a7
 
1079a7
 
1079a7
-class _TransportCertCache:
1079a7
+class _KraConfigCache:
1079a7
+    """The KRA config cache stores vaultconfig-show result.
1079a7
+    """
1079a7
     def __init__(self):
1079a7
         self._dirname = os.path.join(
1079a7
-                USER_CACHE_PATH, 'ipa', 'kra-transport-certs'
1079a7
+            constants.USER_CACHE_PATH, 'ipa', 'kra-config'
1079a7
         )
1079a7
 
1079a7
     def _get_filename(self, domain):
1079a7
-        basename = DNSName(domain).ToASCII() + '.pem'
1079a7
+        basename = DNSName(domain).ToASCII() + '.json'
1079a7
         return os.path.join(self._dirname, basename)
1079a7
 
1079a7
-    def load_cert(self, domain):
1079a7
-        """Load cert from cache
1079a7
+    def load(self, domain):
1079a7
+        """Load config from cache
1079a7
 
1079a7
         :param domain: IPA domain
1079a7
-        :return: cryptography.x509.Certificate or None
1079a7
+        :return: dict or None
1079a7
         """
1079a7
         filename = self._get_filename(domain)
1079a7
         try:
1079a7
             try:
1079a7
-                return x509.load_certificate_from_file(filename)
1079a7
-            except EnvironmentError as e:
1079a7
+                with open(filename) as f:
1079a7
+                    return json.load(f)
1079a7
+            except OSError as e:
1079a7
                 if e.errno != errno.ENOENT:
1079a7
                     raise
1079a7
         except Exception:
1079a7
             logger.warning("Failed to load %s", filename, exc_info=True)
1079a7
         return None
1079a7
 
1079a7
-    def store_cert(self, domain, transport_cert):
1079a7
-        """Store a new cert or override existing cert
1079a7
+    def store(self, domain, response):
1079a7
+        """Store config in cache
1079a7
 
1079a7
         :param domain: IPA domain
1079a7
-        :param transport_cert: cryptography.x509.Certificate
1079a7
-        :return: True if cert was stored successfully
1079a7
+        :param config: ipa vaultconfig-show response
1079a7
+        :return: True if config was stored successfully
1079a7
         """
1079a7
+        config = response['result'].copy()
1079a7
+        # store certificate as PEM-encoded ASCII
1079a7
+        config['transport_cert'] = ssl.DER_cert_to_PEM_cert(
1079a7
+            config['transport_cert']
1079a7
+        )
1079a7
         filename = self._get_filename(domain)
1079a7
-        pem = transport_cert.public_bytes(serialization.Encoding.PEM)
1079a7
         try:
1079a7
             try:
1079a7
                 os.makedirs(self._dirname)
1079a7
@@ -589,9 +597,9 @@ class _TransportCertCache:
1079a7
                 if e.errno != errno.EEXIST:
1079a7
                     raise
1079a7
             with tempfile.NamedTemporaryFile(dir=self._dirname, delete=False,
1079a7
-                                             mode='wb') as f:
1079a7
+                                             mode='w') as f:
1079a7
                 try:
1079a7
-                    f.write(pem)
1079a7
+                    json.dump(config, f)
1079a7
                     ipautil.flush_sync(f)
1079a7
                     f.close()
1079a7
                     os.rename(f.name, filename)
1079a7
@@ -604,8 +612,8 @@ class _TransportCertCache:
1079a7
         else:
1079a7
             return True
1079a7
 
1079a7
-    def remove_cert(self, domain):
1079a7
-        """Remove a cert from cache, ignores errors
1079a7
+    def remove(self, domain):
1079a7
+        """Remove a config from cache, ignores errors
1079a7
 
1079a7
         :param domain: IPA domain
1079a7
         :return: True if cert was found and removed
1079a7
@@ -621,7 +629,7 @@ class _TransportCertCache:
1079a7
             return True
1079a7
 
1079a7
 
1079a7
-_transport_cert_cache = _TransportCertCache()
1079a7
+_kra_config_cache = _KraConfigCache()
1079a7
 
1079a7
 
1079a7
 @register(override=True, no_fail=True)
1079a7
@@ -636,13 +644,8 @@ class vaultconfig_show(MethodOverride):
1079a7
 
1079a7
         response = super(vaultconfig_show, self).forward(*args, **options)
1079a7
 
1079a7
-        # cache transport certificate
1079a7
-        transport_cert = x509.load_der_x509_certificate(
1079a7
-                response['result']['transport_cert'])
1079a7
-
1079a7
-        _transport_cert_cache.store_cert(
1079a7
-            self.api.env.domain, transport_cert
1079a7
-        )
1079a7
+        # cache config
1079a7
+        _kra_config_cache.store(self.api.env.domain, response)
1079a7
 
1079a7
         if file:
1079a7
             with open(file, 'wb') as f:
1079a7
@@ -652,10 +655,54 @@ class vaultconfig_show(MethodOverride):
1079a7
 
1079a7
 
1079a7
 class ModVaultData(Local):
1079a7
-    def _generate_session_key(self):
1079a7
-        key_length = max(algorithms.TripleDES.key_sizes)
1079a7
-        algo = algorithms.TripleDES(os.urandom(key_length // 8))
1079a7
-        return algo
1079a7
+    def _generate_session_key(self, name):
1079a7
+        if name not in constants.VAULT_WRAPPING_SUPPORTED_ALGOS:
1079a7
+            msg = _("{algo} is not a supported vault wrapping algorithm")
1079a7
+            raise errors.ValidationError(msg.format(algo=repr(name)))
1079a7
+        if name == constants.VAULT_WRAPPING_AES128_CBC:
1079a7
+            return algorithms.AES(os.urandom(128 // 8))
1079a7
+        elif name == constants.VAULT_WRAPPING_3DES:
1079a7
+            return algorithms.TripleDES(os.urandom(196 // 8))
1079a7
+        else:
1079a7
+            # unreachable
1079a7
+            raise ValueError(name)
1079a7
+
1079a7
+    def _get_vaultconfig(self, force_refresh=False):
1079a7
+        config = None
1079a7
+        if not force_refresh:
1079a7
+            config = _kra_config_cache.load(self.api.env.domain)
1079a7
+        if config is None:
1079a7
+            # vaultconfig_show also caches data
1079a7
+            response = self.api.Command.vaultconfig_show()
1079a7
+            config = response['result']
1079a7
+            transport_cert = x509.load_der_x509_certificate(
1079a7
+                config['transport_cert']
1079a7
+            )
1079a7
+        else:
1079a7
+            # cached JSON uses PEM-encoded ASCII string
1079a7
+            transport_cert = x509.load_pem_x509_certificate(
1079a7
+                config['transport_cert'].encode('ascii')
1079a7
+            )
1079a7
+
1079a7
+        default_algo = config.get('wrapping_default_algorithm')
1079a7
+        if default_algo is None:
1079a7
+            # old server
1079a7
+            wrapping_algo = constants.VAULT_WRAPPING_AES128_CBC
1079a7
+        elif default_algo in constants.VAULT_WRAPPING_SUPPORTED_ALGOS:
1079a7
+            # try to use server default
1079a7
+            wrapping_algo = default_algo
1079a7
+        else:
1079a7
+            # prefer server's sorting order
1079a7
+            for algo in config['wrapping_supported_algorithms']:
1079a7
+                if algo in constants.VAULT_WRAPPING_SUPPORTED_ALGOS:
1079a7
+                    wrapping_algo = algo
1079a7
+                    break
1079a7
+            else:
1079a7
+                raise errors.ValidationError(
1079a7
+                    "No overlapping wrapping algorithm between server and "
1079a7
+                    "client."
1079a7
+                )
1079a7
+        return transport_cert, wrapping_algo
1079a7
 
1079a7
     def _do_internal(self, algo, transport_cert, raise_unexpected,
1079a7
                      *args, **options):
1079a7
@@ -675,29 +722,23 @@ class ModVaultData(Local):
1079a7
         except (errors.InternalError,
1079a7
                 errors.ExecutionError,
1079a7
                 errors.GenericError):
1079a7
-            _transport_cert_cache.remove_cert(self.api.env.domain)
1079a7
+            _kra_config_cache.remove(self.api.env.domain)
1079a7
             if raise_unexpected:
1079a7
                 raise
1079a7
         return None
1079a7
 
1079a7
-    def internal(self, algo, *args, **options):
1079a7
+    def internal(self, algo, transport_cert, *args, **options):
1079a7
         """
1079a7
         Calls the internal counterpart of the command.
1079a7
         """
1079a7
-        domain = self.api.env.domain
1079a7
-
1079a7
         # try call with cached transport certificate
1079a7
-        transport_cert = _transport_cert_cache.load_cert(domain)
1079a7
-        if transport_cert is not None:
1079a7
-            result = self._do_internal(algo, transport_cert, False,
1079a7
+        result = self._do_internal(algo, transport_cert, False,
1079a7
                                        *args, **options)
1079a7
-            if result is not None:
1079a7
-                return result
1079a7
+        if result is not None:
1079a7
+            return result
1079a7
 
1079a7
         # retrieve transport certificate (cached by vaultconfig_show)
1079a7
-        response = self.api.Command.vaultconfig_show()
1079a7
-        transport_cert = x509.load_der_x509_certificate(
1079a7
-            response['result']['transport_cert'])
1079a7
+        transport_cert = self._get_vaultconfig(force_refresh=True)[0]
1079a7
         # call with the retrieved transport certificate
1079a7
         return self._do_internal(algo, transport_cert, True,
1079a7
                                  *args, **options)
1079a7
@@ -777,7 +818,7 @@ class vault_archive(ModVaultData):
1079a7
     def _wrap_data(self, algo, json_vault_data):
1079a7
         """Encrypt data with wrapped session key and transport cert
1079a7
 
1079a7
-        :param bytes algo: wrapping algorithm instance
1079a7
+        :param algo: wrapping algorithm instance
1079a7
         :param bytes json_vault_data: dumped vault data
1079a7
         :return:
1079a7
         """
1079a7
@@ -929,15 +970,24 @@ class vault_archive(ModVaultData):
1079a7
 
1079a7
         json_vault_data = json.dumps(vault_data).encode('utf-8')
1079a7
 
1079a7
+        # get config
1079a7
+        transport_cert, wrapping_algo = self._get_vaultconfig()
1079a7
+        # let options override wrapping algo
1079a7
+        # For backwards compatibility do not send old legacy wrapping algo
1079a7
+        # to server. Only send the option when non-3DES is used.
1079a7
+        wrapping_algo = options.pop('wrapping_algo', wrapping_algo)
1079a7
+        if wrapping_algo != constants.VAULT_WRAPPING_3DES:
1079a7
+            options['wrapping_algo'] = wrapping_algo
1079a7
+
1079a7
         # generate session key
1079a7
-        algo = self._generate_session_key()
1079a7
+        algo = self._generate_session_key(wrapping_algo)
1079a7
         # wrap vault data
1079a7
         nonce, wrapped_vault_data = self._wrap_data(algo, json_vault_data)
1079a7
         options.update(
1079a7
             nonce=nonce,
1079a7
             vault_data=wrapped_vault_data
1079a7
         )
1079a7
-        return self.internal(algo, *args, **options)
1079a7
+        return self.internal(algo, transport_cert, *args, **options)
1079a7
 
1079a7
 
1079a7
 @register(no_fail=True)
1079a7
@@ -1061,10 +1111,19 @@ class vault_retrieve(ModVaultData):
1079a7
         vault = self.api.Command.vault_show(*args, **options)['result']
1079a7
         vault_type = vault['ipavaulttype'][0]
1079a7
 
1079a7
+        # get config
1079a7
+        transport_cert, wrapping_algo = self._get_vaultconfig()
1079a7
+        # let options override wrapping algo
1079a7
+        # For backwards compatibility do not send old legacy wrapping algo
1079a7
+        # to server. Only send the option when non-3DES is used.
1079a7
+        wrapping_algo = options.pop('wrapping_algo', wrapping_algo)
1079a7
+        if wrapping_algo != constants.VAULT_WRAPPING_3DES:
1079a7
+            options['wrapping_algo'] = wrapping_algo
1079a7
+
1079a7
         # generate session key
1079a7
-        algo = self._generate_session_key()
1079a7
+        algo = self._generate_session_key(wrapping_algo)
1079a7
         # send retrieval request to server
1079a7
-        response = self.internal(algo, *args, **options)
1079a7
+        response = self.internal(algo, transport_cert, *args, **options)
1079a7
         # unwrap data with session key
1079a7
         vault_data = self._unwrap_response(
1079a7
             algo,
1079a7
diff --git a/ipalib/capabilities.py b/ipalib/capabilities.py
1079a7
index 55b84aa6bc73d583e7bd5d03d2f4f1cc5c8e7c0b..4d8ae408bf67c280d27ce494baa9db9aaff0cd69 100644
1079a7
--- a/ipalib/capabilities.py
1079a7
+++ b/ipalib/capabilities.py
1079a7
@@ -54,6 +54,10 @@ capabilities = dict(
1079a7
 
1079a7
     # dns_name_values: dnsnames as objects
1079a7
     dns_name_values=u'2.88',
1079a7
+
1079a7
+    # vault supports aes key wrapping
1079a7
+    vault_aes_keywrap='2.246'
1079a7
+
1079a7
 )
1079a7
 
1079a7
 
1079a7
diff --git a/ipalib/constants.py b/ipalib/constants.py
1079a7
index 9f19b0f9941ba5068f1e6c218092e3b76fdc7599..11171b2e8aeb6f7306299b2bd7db3a3f39d29d4a 100644
1079a7
--- a/ipalib/constants.py
1079a7
+++ b/ipalib/constants.py
1079a7
@@ -374,3 +374,15 @@ KRA_TRACKING_REQS = {
1079a7
 }
1079a7
 
1079a7
 ALLOWED_NETBIOS_CHARS = string.ascii_uppercase + string.digits + '-'
1079a7
+
1079a7
+# vault data wrapping algorithms
1079a7
+VAULT_WRAPPING_3DES = 'des-ede3-cbc'
1079a7
+VAULT_WRAPPING_AES128_CBC = 'aes-128-cbc'
1079a7
+VAULT_WRAPPING_SUPPORTED_ALGOS = (
1079a7
+    # old default was 3DES
1079a7
+    VAULT_WRAPPING_3DES,
1079a7
+    # supported since pki-kra >= 10.4
1079a7
+    VAULT_WRAPPING_AES128_CBC,
1079a7
+)
1079a7
+# 3DES for backwards compatibility
1079a7
+VAULT_WRAPPING_DEFAULT_ALGO = VAULT_WRAPPING_3DES
1079a7
diff --git a/ipaserver/plugins/vault.py b/ipaserver/plugins/vault.py
1079a7
index aebac7dff7bb9d183c6012cc685577d476e18c4e..4d40f66c6a793a831e91c5fe25c8b5277cbd1972 100644
1079a7
--- a/ipaserver/plugins/vault.py
1079a7
+++ b/ipaserver/plugins/vault.py
1079a7
@@ -23,6 +23,10 @@ from ipalib.frontend import Command, Object
1079a7
 from ipalib import api, errors
1079a7
 from ipalib import Bytes, Flag, Str, StrEnum
1079a7
 from ipalib import output
1079a7
+from ipalib.constants import (
1079a7
+    VAULT_WRAPPING_SUPPORTED_ALGOS, VAULT_WRAPPING_DEFAULT_ALGO,
1079a7
+    VAULT_WRAPPING_3DES, VAULT_WRAPPING_AES128_CBC,
1079a7
+)
1079a7
 from ipalib.crud import PKQuery, Retrieve
1079a7
 from ipalib.parameters import Principal
1079a7
 from ipalib.plugable import Registry
1079a7
@@ -39,14 +43,8 @@ from ipaserver.masters import is_service_enabled
1079a7
 if api.env.in_server:
1079a7
     import pki.account
1079a7
     import pki.key
1079a7
-    # pylint: disable=no-member
1079a7
-    try:
1079a7
-        # pki >= 10.4.0
1079a7
-        from pki.crypto import DES_EDE3_CBC_OID
1079a7
-    except ImportError:
1079a7
-        DES_EDE3_CBC_OID = pki.key.KeyClient.DES_EDE3_CBC_OID
1079a7
-    # pylint: enable=no-member
1079a7
-
1079a7
+    from pki.crypto import DES_EDE3_CBC_OID
1079a7
+    from pki.crypto import AES_128_CBC_OID
1079a7
 
1079a7
 if six.PY3:
1079a7
     unicode = str
1079a7
@@ -652,6 +652,20 @@ class vault(LDAPObject):
1079a7
         ),
1079a7
     )
1079a7
 
1079a7
+    def _translate_algorithm(self, name):
1079a7
+        if name is None:
1079a7
+            name = VAULT_WRAPPING_DEFAULT_ALGO
1079a7
+        if name not in VAULT_WRAPPING_SUPPORTED_ALGOS:
1079a7
+            msg = _("{algo} is not a supported vault wrapping algorithm")
1079a7
+            raise errors.ValidationError(msg.format(algo=name))
1079a7
+        if name == VAULT_WRAPPING_3DES:
1079a7
+            return DES_EDE3_CBC_OID
1079a7
+        elif name == VAULT_WRAPPING_AES128_CBC:
1079a7
+            return AES_128_CBC_OID
1079a7
+        else:
1079a7
+            # unreachable
1079a7
+            raise ValueError(name)
1079a7
+
1079a7
     def get_dn(self, *keys, **options):
1079a7
         """
1079a7
         Generates vault DN from parameters.
1079a7
@@ -992,14 +1006,18 @@ class vaultconfig_show(Retrieve):
1079a7
     )
1079a7
 
1079a7
     def execute(self, *args, **options):
1079a7
-
1079a7
         if not self.api.Command.kra_is_enabled()['result']:
1079a7
             raise errors.InvocationError(
1079a7
                 format=_('KRA service is not enabled'))
1079a7
 
1079a7
+        config = dict(
1079a7
+            wrapping_supported_algorithms=VAULT_WRAPPING_SUPPORTED_ALGOS,
1079a7
+            wrapping_default_algorithm=VAULT_WRAPPING_DEFAULT_ALGO,
1079a7
+        )
1079a7
+
1079a7
         with self.api.Backend.kra.get_client() as kra_client:
1079a7
             transport_cert = kra_client.system_certs.get_transport_cert()
1079a7
-            config = {'transport_cert': transport_cert.binary}
1079a7
+            config['transport_cert'] = transport_cert.binary
1079a7
 
1079a7
         self.api.Object.config.show_servroles_attributes(
1079a7
             config, "KRA server", **options)
1079a7
@@ -1029,6 +1047,13 @@ class vault_archive_internal(PKQuery):
1079a7
             'nonce',
1079a7
             doc=_('Nonce'),
1079a7
         ),
1079a7
+        StrEnum(
1079a7
+            'wrapping_algo?',
1079a7
+            doc=_('Key wrapping algorithm'),
1079a7
+            values=VAULT_WRAPPING_SUPPORTED_ALGOS,
1079a7
+            default=VAULT_WRAPPING_DEFAULT_ALGO,
1079a7
+            autofill=True,
1079a7
+        ),
1079a7
     )
1079a7
 
1079a7
     has_output = output.standard_entry
1079a7
@@ -1045,6 +1070,9 @@ class vault_archive_internal(PKQuery):
1079a7
         nonce = options.pop('nonce')
1079a7
         wrapped_session_key = options.pop('session_key')
1079a7
 
1079a7
+        wrapping_algo = options.pop('wrapping_algo', None)
1079a7
+        algorithm_oid = self.obj._translate_algorithm(wrapping_algo)
1079a7
+
1079a7
         # retrieve vault info
1079a7
         vault = self.api.Command.vault_show(*args, **options)['result']
1079a7
 
1079a7
@@ -1071,7 +1099,7 @@ class vault_archive_internal(PKQuery):
1079a7
                 pki.key.KeyClient.PASS_PHRASE_TYPE,
1079a7
                 wrapped_vault_data,
1079a7
                 wrapped_session_key,
1079a7
-                algorithm_oid=DES_EDE3_CBC_OID,
1079a7
+                algorithm_oid=algorithm_oid,
1079a7
                 nonce_iv=nonce,
1079a7
             )
1079a7
 
1079a7
@@ -1098,6 +1126,13 @@ class vault_retrieve_internal(PKQuery):
1079a7
             'session_key',
1079a7
             doc=_('Session key wrapped with transport certificate'),
1079a7
         ),
1079a7
+        StrEnum(
1079a7
+            'wrapping_algo?',
1079a7
+            doc=_('Key wrapping algorithm'),
1079a7
+            values=VAULT_WRAPPING_SUPPORTED_ALGOS,
1079a7
+            default=VAULT_WRAPPING_DEFAULT_ALGO,
1079a7
+            autofill=True,
1079a7
+        ),
1079a7
     )
1079a7
 
1079a7
     has_output = output.standard_entry
1079a7
@@ -1112,6 +1147,9 @@ class vault_retrieve_internal(PKQuery):
1079a7
 
1079a7
         wrapped_session_key = options.pop('session_key')
1079a7
 
1079a7
+        wrapping_algo = options.pop('wrapping_algo', None)
1079a7
+        algorithm_oid = self.obj._translate_algorithm(wrapping_algo)
1079a7
+
1079a7
         # retrieve vault info
1079a7
         vault = self.api.Command.vault_show(*args, **options)['result']
1079a7
 
1079a7
@@ -1132,6 +1170,9 @@ class vault_retrieve_internal(PKQuery):
1079a7
 
1079a7
             key_info = response.key_infos[0]
1079a7
 
1079a7
+            # XXX hack
1079a7
+            kra_client.keys.encrypt_alg_oid = algorithm_oid
1079a7
+
1079a7
             # retrieve encrypted data from KRA
1079a7
             key = kra_client.keys.retrieve_key(
1079a7
                 key_info.get_key_id(),
1079a7
-- 
1079a7
2.34.1
1079a7