Skip to content

Commit

Permalink
crypto: Use libdnf crypto API instead of using GnuPG/GpgME
Browse files Browse the repository at this point in the history
- It is compatible with the new librepo. The new librepo may use
  a different backend than GpgME internally.

- Removes dependency on GnuPG/gpgme.
  • Loading branch information
jrohel committed Aug 24, 2023
1 parent 33fde24 commit 0f89c91
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 122 deletions.
2 changes: 1 addition & 1 deletion VERSION.cmake
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
set (DEFAULT_DNF_VERSION "4.16.2")
set (DEFAULT_DNF_VERSION "4.17.0")

if(DEFINED DNF_VERSION)
if(NOT ${DEFAULT_DNF_VERSION} STREQUAL ${DNF_VERSION})
Expand Down
6 changes: 2 additions & 4 deletions dnf.spec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
%define __cmake_in_source_build 1

# default dependencies
%global hawkey_version 0.66.0
%global hawkey_version 0.71.0
%global libcomps_version 0.1.8
%global libmodulemd_version 2.9.3
%global rpm_version 4.14.0
Expand Down Expand Up @@ -67,7 +67,7 @@
It supports RPMs, modules and comps groups & environments.

Name: dnf
Version: 4.16.2
Version: 4.17.0
Release: 1%{?dist}
Summary: %{pkg_summary}
# For a breakdown of the licensing, see PACKAGE-LICENSING
Expand Down Expand Up @@ -130,8 +130,6 @@ BuildRequires: python3-libcomps >= %{libcomps_version}
BuildRequires: python3-libdnf
BuildRequires: libmodulemd >= %{libmodulemd_version}
Requires: libmodulemd >= %{libmodulemd_version}
BuildRequires: python3-gpg
Requires: python3-gpg
Requires: %{name}-data = %{version}-%{release}
%if 0%{?fedora}
Recommends: deltarpm
Expand Down
118 changes: 28 additions & 90 deletions dnf/crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,74 +22,20 @@
from __future__ import absolute_import
from __future__ import unicode_literals
from dnf.i18n import _
import libdnf.repo
import contextlib
import dnf.pycomp
import dnf.util
import dnf.yum.misc
import io
import logging
import os
import tempfile

try:
from gpg import Context
from gpg import Data
except ImportError:
import gpgme


class Context(object):
def __init__(self):
self.__dict__["ctx"] = gpgme.Context()

def __enter__(self):
return self

def __exit__(self, type, value, tb):
pass

@property
def armor(self):
return self.ctx.armor

@armor.setter
def armor(self, value):
self.ctx.armor = value

def op_import(self, key_fo):
if isinstance(key_fo, basestring):
key_fo = io.BytesIO(key_fo)
self.ctx.import_(key_fo)

def op_export(self, pattern, mode, keydata):
self.ctx.export(pattern, keydata)

def __getattr__(self, name):
return getattr(self.ctx, name)


class Data(object):
def __init__(self):
self.__dict__["buf"] = io.BytesIO()

def __enter__(self):
return self

def __exit__(self, type, value, tb):
pass

def read(self):
return self.buf.getvalue()

def __getattr__(self, name):
return getattr(self.buf, name)


GPG_HOME_ENV = 'GNUPGHOME'
logger = logging.getLogger('dnf')


def _extract_signing_subkey(key):
# :deprecated, undocumented
""" Deprecated feature. It was used internally. It is no longer used. """
return dnf.util.first(subkey for subkey in key.subkeys if subkey.can_sign)


Expand All @@ -99,6 +45,11 @@ def _printable_fingerprint(fpr_hex):


def import_repo_keys(repo):
# :deprecated, undocumented
""" Deprecated function. Please do not use.
It was used internally. In 2018, the code was rewritten into libdnf. This code is no longer used.
It was broken in 2018 - the `repo._key_import._confirm` method now needs 5 arguments.
It is now fixed and marked as deprecated. Please do not use. """
gpgdir = repo._pubring_dir
known_keys = keyids_from_pubring(gpgdir)
for keyurl in repo.gpgkey:
Expand All @@ -107,7 +58,8 @@ def import_repo_keys(repo):
if keyid in known_keys:
logger.debug(_('repo %s: 0x%s already imported'), repo.id, keyid)
continue
if not repo._key_import._confirm(keyinfo):
if not repo._key_import._confirm(
keyid, keyinfo.userid, keyinfo.fingerprint, keyinfo.url, keyinfo.timestamp):
continue
dnf.yum.misc.import_key_to_pubring(
keyinfo.raw_key, keyinfo.short_id, gpgdir=gpgdir,
Expand All @@ -116,16 +68,12 @@ def import_repo_keys(repo):


def keyids_from_pubring(gpgdir):
if not os.path.exists(gpgdir):
return []

with pubring_dir(gpgdir), Context() as ctx:
keyids = []
for k in ctx.keylist():
subkey = _extract_signing_subkey(k)
if subkey is not None:
keyids.append(subkey.keyid)
return keyids
# :deprecated, undocumented
""" Used internally by deprecated function `import_repo_keys` """
keyids = []
for keyid in libdnf.repo.keyidsFromPubring(gpgdir):
keyids.append(keyid)
return keyids


def log_key_import(keyinfo):
Expand All @@ -148,6 +96,8 @@ def log_dns_key_import(keyinfo, dns_result):

@contextlib.contextmanager
def pubring_dir(pubring_dir):
# :deprecated, undocumented
""" Deprecated feature. It was used internally. It is no longer used. """
orig = os.environ.get(GPG_HOME_ENV, None)
os.environ[GPG_HOME_ENV] = pubring_dir
try:
Expand All @@ -160,22 +110,10 @@ def pubring_dir(pubring_dir):


def rawkey2infos(key_fo):
pb_dir = tempfile.mkdtemp()
keyinfos = []
with pubring_dir(pb_dir), Context() as ctx:
ctx.op_import(key_fo)
for key in ctx.keylist():
subkey = _extract_signing_subkey(key)
if subkey is None:
continue
keyinfos.append(Key(key, subkey))
ctx.armor = True
for info in keyinfos:
with Data() as sink:
ctx.op_export(info.id_, 0, sink)
sink.seek(0, os.SEEK_SET)
info.raw_key = sink.read()
dnf.util.rm_rf(pb_dir)
keys = libdnf.repo.Key.keysFromFd(key_fo.fileno())
for key in keys:
keyinfos.append(Key(key))
return keyinfos


Expand All @@ -190,13 +128,13 @@ def retrieve(keyurl, repo=None):


class Key(object):
def __init__(self, key, subkey):
self.id_ = subkey.keyid
self.fingerprint = subkey.fpr
self.raw_key = None
self.timestamp = subkey.timestamp
self.url = None
self.userid = key.uids[0].uid
def __init__(self, repokey):
self.id_ = repokey.getId()
self.fingerprint = repokey.getFingerprint()
self.raw_key = bytes(repokey.getAsciiArmoredKey(), 'utf-8')
self.timestamp = repokey.getTimestamp()
self.url = repokey.getUrl()
self.userid = repokey.getUserId()

@property
def short_id(self):
Expand Down
43 changes: 16 additions & 27 deletions dnf/yum/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from __future__ import unicode_literals
from dnf.pycomp import base64_decodebytes, basestring, unicode
from stat import *
import libdnf.repo
import libdnf.utils
import dnf.const
import dnf.crypto
Expand Down Expand Up @@ -159,38 +160,26 @@ def keyInstalled(ts, keyid, timestamp):


def import_key_to_pubring(rawkey, keyid, gpgdir=None, make_ro_copy=True):
# :deprecated, undocumented
""" Used internally by deprecated function `import_repo_keys` """

if not os.path.exists(gpgdir):
os.makedirs(gpgdir)

with dnf.crypto.pubring_dir(gpgdir), dnf.crypto.Context() as ctx:
# import the key
with open(os.path.join(gpgdir, 'gpg.conf'), 'wb') as fp:
fp.write(b'')
ctx.op_import(rawkey)

if make_ro_copy:

rodir = gpgdir + '-ro'
if not os.path.exists(rodir):
os.makedirs(rodir, mode=0o755)
for f in glob.glob(gpgdir + '/*'):
basename = os.path.basename(f)
ro_f = rodir + '/' + basename
shutil.copy(f, ro_f)
os.chmod(ro_f, 0o755)
# yes it is this stupid, why do you ask?
opts = """lock-never
no-auto-check-trustdb
trust-model direct
no-expensive-trust-checks
no-permission-warning
preserve-permissions
"""
with open(os.path.join(rodir, 'gpg.conf'), 'w', 0o755) as fp:
fp.write(opts)
# import the key
libdnf.repo.importKeyToPubring(str(rawkey, 'utf-8'), gpgdir)

if make_ro_copy:
rodir = gpgdir + '-ro'
if not os.path.exists(rodir):
os.makedirs(rodir, mode=0o755)
for f in glob.glob(gpgdir + '/*'):
basename = os.path.basename(f)
ro_f = rodir + '/' + basename
shutil.copy(f, ro_f)
os.chmod(ro_f, 0o755)

return True
return True


def getCacheDir():
Expand Down

0 comments on commit 0f89c91

Please sign in to comment.