From 9c6a6ded0161aea8bf87c7d2f06eaa8b56520719 Mon Sep 17 00:00:00 2001
From: Christian Heimes <cheimes@redhat.com>
Date: Wed, 19 Aug 2015 15:57:07 +0200
Subject: [PATCH] Replace legacy Python base64 invocations with Py3-safe code

Provide custom b64encode() and b64decode() functions that are able to
handle ASCII text on Python 3, too. Replace deprecated base64
functions base64.encodestring() and base64.decodestring() with
the custom functions.

https://fedorahosted.org/pki/ticket/1102
---
 base/common/python/pki/encoder.py           | 30 +++++++++++++++++++++++++++++
 base/common/python/pki/key.py               | 19 +++++++++---------
 base/common/python/pki/systemcert.py        |  5 +++--
 base/kra/functional/drmclient_deprecated.py | 16 ++++++++-------
 base/kra/functional/drmtest.py              | 19 +++++++++---------
 5 files changed, 61 insertions(+), 28 deletions(-)

diff --git a/base/common/python/pki/encoder.py b/base/common/python/pki/encoder.py
index bf5d2e473522f2e2348da535ae4029633b5eee46..040fd9fd8b291d96c0044deb3ba778fef4957834 100644
--- a/base/common/python/pki/encoder.py
+++ b/base/common/python/pki/encoder.py
@@ -1,11 +1,41 @@
 from __future__ import absolute_import
+
+import base64
 import json
+
+import six
 from six import iteritems, itervalues
 
 TYPES = {}
 NOTYPES = {}
 
 
+def b64encode(data):
+    """base64 encode data
+
+    Python 3's base64.b64encode() doesn't support ASCII text.
+
+    :param data: data as bytes or ASCII text
+    :type data: str, bytes
+    :rtype: bytes
+    """
+    if isinstance(data, six.text_type):
+        data = data.encode('ascii')
+    return base64.b64encode(data)
+
+
+def b64decode(data):
+    """base64 decode data
+
+    :param data: data as bytes or ASCII text
+    :type data: str, bytes
+    :rtype: bytes
+    """
+    if isinstance(data, six.text_type):
+        data = data.encode('ascii')
+    return base64.b64decode(data)
+
+
 class CustomTypeEncoder(json.JSONEncoder):
     """
     A custom JSONEncoder class that knows how to encode core custom
diff --git a/base/common/python/pki/key.py b/base/common/python/pki/key.py
index 83883dd31e3919e3ba228a9065db7e808ce15923..c4972210fe1eac3d97d792ae70fc965908fd1638 100644
--- a/base/common/python/pki/key.py
+++ b/base/common/python/pki/key.py
@@ -26,7 +26,6 @@ KeyRequestClient REST API on a DRM
 """
 from __future__ import absolute_import
 from __future__ import print_function
-import base64
 import json
 
 from six import iteritems
@@ -89,9 +88,9 @@ class Key(object):
 
     def __init__(self, key_data):
         """ Constructor """
-        self.encrypted_data = base64.decodestring(
+        self.encrypted_data = encoder.b64decode(
             key_data.wrapped_private_data)
-        self.nonce_data = base64.decodestring(key_data.nonce_data)
+        self.nonce_data = encoder.b64decode(key_data.nonce_data)
         self.algorithm = key_data.algorithm
         self.size = key_data.size
 
@@ -133,7 +132,7 @@ class KeyInfo(object):
             else:
                 setattr(key_info, k, v)
         if key_info.public_key is not None:
-            key_info.public_key = base64.decodestring(key_info.public_key)
+            key_info.public_key = encoder.b64decode(key_info.public_key)
         return key_info
 
     def get_key_id(self):
@@ -584,7 +583,7 @@ class KeyClient(object):
             raise TypeError("Must specify Client Key ID")
 
         if trans_wrapped_session_key is not None:
-            twsk = base64.encodestring(trans_wrapped_session_key)
+            twsk = encoder.b64encode(trans_wrapped_session_key)
             # noinspection PyUnusedLocal
             request = SymKeyGenerationRequest(
                 client_key_id=client_key_id,
@@ -764,9 +763,9 @@ class KeyClient(object):
         if not nonce_iv:
             raise TypeError('Missing nonce IV')
 
-        data = base64.encodestring(encrypted_data)
-        twsk = base64.encodestring(wrapped_session_key)
-        symkey_params = base64.encodestring(nonce_iv)
+        data = encoder.b64encode(encrypted_data)
+        twsk = encoder.b64encode(wrapped_session_key)
+        symkey_params = encoder.b64encode(nonce_iv)
 
         request = KeyArchivalRequest(client_key_id=client_key_id,
                                      data_type=data_type,
@@ -806,7 +805,7 @@ class KeyClient(object):
         if pki_archive_options is None:
             raise TypeError("No data provided to be archived")
 
-        data = base64.encodestring(pki_archive_options)
+        data = encoder.b64encode(pki_archive_options)
         request = KeyArchivalRequest(client_key_id=client_key_id,
                                      data_type=data_type,
                                      pki_archive_options=data,
@@ -915,7 +914,7 @@ class KeyClient(object):
         request = KeyRecoveryRequest(
             key_id=key_id,
             request_id=request_id,
-            trans_wrapped_session_key=base64.encodestring(
+            trans_wrapped_session_key=encoder.b64encode(
                 trans_wrapped_session_key))
 
         key = self.retrieve_key_data(request)
diff --git a/base/common/python/pki/systemcert.py b/base/common/python/pki/systemcert.py
index 4adc2f18e5e7d218df2696982faf6851587cde31..6254b08cdeb1b15a04dad4cf9dcaeb175be356fb 100644
--- a/base/common/python/pki/systemcert.py
+++ b/base/common/python/pki/systemcert.py
@@ -22,9 +22,10 @@
 Module containing the Python client classes for the SystemCert REST API
 """
 from __future__ import absolute_import
-import base64
+
 import pki
 from pki.cert import CertData
+from pki.encoder import b64decode
 
 
 class SystemCertClient(object):
@@ -55,6 +56,6 @@ class SystemCertClient(object):
 
         pem = cert_data.encoded
         b64 = pem[len(pki.CERT_HEADER):len(pem) - len(pki.CERT_FOOTER)]
-        cert_data.binary = base64.decodestring(b64)
+        cert_data.binary = b64decode(b64)
 
         return cert_data
diff --git a/base/kra/functional/drmclient_deprecated.py b/base/kra/functional/drmclient_deprecated.py
index e558073f8370f28e13758673fff475422f7f8735..8e8303cd413f3f568b39e5db2f5701178922d39a 100644
--- a/base/kra/functional/drmclient_deprecated.py
+++ b/base/kra/functional/drmclient_deprecated.py
@@ -44,7 +44,6 @@ from ipalib.errors import NetworkError, CertificateOperationError
 from urllib import urlencode, quote_plus
 from datetime import datetime
 import logging
-import base64
 import six
 from six.moves import http_client  # pylint: disable=F0401
 
@@ -52,6 +51,9 @@ CERT_HEADER = "-----BEGIN NEW CERTIFICATE REQUEST-----"
 CERT_FOOTER = "-----END NEW CERTIFICATE REQUEST-----"
 
 
+from pki.encoder import b64decode, b64encode
+
+
 def _(string):
     return string
 
@@ -949,7 +951,7 @@ class KRA:
 
         # wrap this key with the transport cert
         public_key = self.transport_cert.subject_public_key_info.public_key
-        wrapped_session_key = base64.b64encode(
+        wrapped_session_key = b64encode(
             nss.pub_wrap_sym_key(
                 self.mechanism,
                 public_key,
@@ -957,7 +959,7 @@ class KRA:
         wrapped_passphrase = None
         if passphrase is not None:
             # wrap passphrase with session key
-            wrapped_session_key = base64.b64encode(
+            wrapped_session_key = b64encode(
                 self.symmetric_wrap(
                     passphrase,
                     session_key))
@@ -982,10 +984,10 @@ class KRA:
 
         if passphrase is None:
             iv = nss.data_to_hex(
-                base64.decodestring(
+                b64decode(
                     parse_result['nonce_data']))
             parse_result['data'] = self.symmetric_unwrap(
-                base64.decodestring(parse_result['wrapped_data']),
+                b64decode(parse_result['wrapped_data']),
                 session_key, iv)
 
         return parse_result
@@ -1090,14 +1092,14 @@ except CertificateOperationError as e:
 # retrieve key
 response = test_kra.retrieve_security_data(request_id)
 print(response)
-print("retrieved data is " + base64.encodestring(response['data']))
+print("retrieved data is " + b64encode(response['data']))
 
 # read original symkey from file
 f = open(work_dir + "/" + symkey_file)
 orig_key = f.read()
 print("orig key is " + orig_key)
 
-if orig_key.strip() == base64.encodestring(response['data']).strip():
+if orig_key.strip() == b64encode(response['data']).strip():
     print("Success: the keys match")
 else:
     print("Failure: keys do not match")
diff --git a/base/kra/functional/drmtest.py b/base/kra/functional/drmtest.py
index dd44c374b6812b94992f6b367d819cec8f07cb6e..4210912150185a0f3082a40a21285a6fc4696c08 100755
--- a/base/kra/functional/drmtest.py
+++ b/base/kra/functional/drmtest.py
@@ -34,7 +34,7 @@ See drmtest.readme.txt.
 
 from __future__ import absolute_import
 from __future__ import print_function
-import base64
+
 import getopt
 import random
 import shutil
@@ -50,6 +50,7 @@ import pki.crypto
 import pki.key as key
 
 from pki.client import PKIConnection
+from pki.encoder import b64decode, b64encode
 from pki.kra import KRAClient
 
 
@@ -72,7 +73,7 @@ def print_key_info(key_info):
     if key_info.public_key is not None:
         print("Public key: ")
         print()
-        pub_key = base64.encodestring(key_info.public_key)
+        pub_key = b64encode(key_info.public_key)
         print(pub_key)
 
 
@@ -80,11 +81,11 @@ def print_key_data(key_data):
     """ Prints the relevant fields of a KeyData object """
     print("Key Algorithm: " + str(key_data.algorithm))
     print("Key Size: " + str(key_data.size))
-    print("Nonce Data: " + base64.encodestring(key_data.nonce_data))
+    print("Nonce Data: " + b64encode(key_data.nonce_data))
     print("Wrapped Private Data: " +
-          base64.encodestring(key_data.encrypted_data))
+          b64encode(key_data.encrypted_data))
     if key_data.data is not None:
-        print("Private Data: " + base64.encodestring(key_data.data))
+        print("Private Data: " + b64encode(key_data.data))
 
 
 def run_test(protocol, hostname, port, client_cert, certdb_dir,
@@ -169,12 +170,12 @@ def run_test(protocol, hostname, port, client_cert, certdb_dir,
     unwrapped_key = crypto.symmetric_unwrap(key_data.encrypted_data,
                                             session_key,
                                             nonce_iv=key_data.nonce_data)
-    key1 = base64.encodestring(unwrapped_key)
+    key1 = b64encode(unwrapped_key)
 
     # Test 7: Recover key without providing trans_wrapped_session_key
     key_data = keyclient.retrieve_key(key_id)
     print_key_data(key_data)
-    key2 = base64.encodestring(key_data.data)
+    key2 = b64encode(key_data.data)
 
     # Test 8 - Confirm that keys returned are the same
     if key1 == key2:
@@ -255,7 +256,7 @@ def run_test(protocol, hostname, port, client_cert, certdb_dir,
 
     response = keyclient.archive_key(client_key_id,
                                      keyclient.SYMMETRIC_KEY_TYPE,
-                                     base64.decodestring(key1),
+                                     b64decode(key1),
                                      key_algorithm=keyclient.AES_ALGORITHM,
                                      key_size=128)
     print_key_request(response.request_info)
@@ -266,7 +267,7 @@ def run_test(protocol, hostname, port, client_cert, certdb_dir,
 
     key_data = keyclient.retrieve_key(key_info.get_key_id())
     print_key_data(key_data)
-    key2 = base64.encodestring(key_data.data)
+    key2 = b64encode(key_data.data)
 
     if key1 == key2:
         print("Success: archived and recovered keys match")
-- 
2.4.3

