From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from lists.gentoo.org (pigeon.gentoo.org [208.92.234.80]) by finch.gentoo.org (Postfix) with ESMTP id 2E6AD1389E2 for ; Mon, 22 Dec 2014 23:11:50 +0000 (UTC) Received: from pigeon.gentoo.org (localhost [127.0.0.1]) by pigeon.gentoo.org (Postfix) with SMTP id 50690E0976; Mon, 22 Dec 2014 23:11:44 +0000 (UTC) Received: from smtp.gentoo.org (smtp.gentoo.org [140.211.166.183]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by pigeon.gentoo.org (Postfix) with ESMTPS id 172D1E0978 for ; Mon, 22 Dec 2014 23:11:43 +0000 (UTC) Received: from oystercatcher.gentoo.org (oystercatcher.gentoo.org [148.251.78.52]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by smtp.gentoo.org (Postfix) with ESMTPS id 0B5B53405D5 for ; Mon, 22 Dec 2014 23:11:42 +0000 (UTC) Received: from localhost.localdomain (localhost [127.0.0.1]) by oystercatcher.gentoo.org (Postfix) with ESMTP id 56D30D27A for ; Mon, 22 Dec 2014 23:11:39 +0000 (UTC) From: "Brian Dolbec" To: gentoo-commits@lists.gentoo.org Content-Transfer-Encoding: 8bit Content-type: text/plain; charset=UTF-8 Reply-To: gentoo-dev@lists.gentoo.org, "Brian Dolbec" Message-ID: <1419093947.b2539fdf0cd2d1b7a7b8c554e079d8fba35fa5a5.dol-sen@gentoo> Subject: [gentoo-commits] proj/gentoo-keys:master commit in: gkeys/ X-VCS-Repository: proj/gentoo-keys X-VCS-Files: gkeys/actions.py gkeys/checks.py gkeys/lib.py X-VCS-Directories: gkeys/ X-VCS-Committer: dol-sen X-VCS-Committer-Name: Brian Dolbec X-VCS-Revision: b2539fdf0cd2d1b7a7b8c554e079d8fba35fa5a5 X-VCS-Branch: master Date: Mon, 22 Dec 2014 23:11:39 +0000 (UTC) Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-Id: Gentoo Linux mail X-BeenThere: gentoo-commits@lists.gentoo.org X-Archives-Salt: 702d0d00-eed3-4827-839a-2c4d4d82cf7f X-Archives-Hash: 6a73b0ddf6ba35115711030793aec250 commit: b2539fdf0cd2d1b7a7b8c554e079d8fba35fa5a5 Author: Brian Dolbec gentoo org> AuthorDate: Fri Nov 28 21:12:02 2014 +0000 Commit: Brian Dolbec gmail com> CommitDate: Sat Dec 20 16:45:47 2014 +0000 URL: http://sources.gentoo.org/gitweb/?p=proj/gentoo-keys.git;a=commit;h=b2539fdf gkeys/checks.py: Add GLEP spec checks GlepCheck: Add pretty_print() Use pretty_print() Fix bug in expiry check Move mapping constants to pyGPG/mappings.py Add combinations of e,a, s to capbilities check. Improve days handling for subkeys. When an infinite expiredate is found in subkeys, reset the dispay days to the primary key days. Improve capabilities checks, fix key_passed logic. In some cases, it reported pass for glep requirements while also reporting the key not having a signing subkey. Fix more tracking logic errors in the final glep pass/fail code. Show 'e' type keys with '----' value for algorithm and bits. Add caps, id reasons to pretty_print output. Make the output formatting consistent using ljust(). Fix pretty_print primary key/subkey final grade text Remove 'a' restriction on checks Not checking authentication subkeys was causing wrong data to be displayed. Even though these subkeys are ignored for the final spec pass/fail. Change a logging.debug from ERROR to WARNING. This delta_t change is not an error. Create speccheck action. Add 'Notice only' to No Encryption capable header. Fix expire tracking for spec pass/fail. Add nick to validation and spec check header. Ignore ['a', 'e'] capabilities for algo, bits failures in the summary --- gkeys/actions.py | 135 ++++++++++++++++++++- gkeys/checks.py | 353 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- gkeys/lib.py | 20 +++- 3 files changed, 494 insertions(+), 14 deletions(-) diff --git a/gkeys/actions.py b/gkeys/actions.py index 6ba63b7..762d305 100644 --- a/gkeys/actions.py +++ b/gkeys/actions.py @@ -22,10 +22,11 @@ from sslfetch.connections import Connector from gkeys.lib import GkeysGPG from gkeys.seedhandler import SeedHandler from gkeys.config import GKEY +from gkeys.checks import SPECCHECK_SUMMARY, convert_pf, convert_yn Available_Actions = ['listseed', 'addseed', 'removeseed', 'moveseed', 'fetchseed', 'listseedfiles', 'listkey', 'installkey', 'removekey', 'movekey', - 'installed', 'importkey', 'verify', 'checkkey', 'sign'] + 'installed', 'importkey', 'verify', 'checkkey', 'sign', 'speccheck'] Action_Options = { 'listseed': ['nick', 'name', 'keydir', 'fingerprint', 'seedfile', 'file'], @@ -43,6 +44,7 @@ Action_Options = { 'verify': ['dest', 'nick', 'name', 'keydir', 'fingerprint', 'category', 'file', 'signature', 'keyring', 'timestamp'], 'checkkey': ['nick', 'name', 'keydir', 'fingerprint', 'category', 'keyring', 'keyid'], 'sign': ['nick', 'name', 'keydir', 'fingerprint', 'file', 'keyring'], + 'speccheck': ['nick', 'name', 'keydir', 'fingerprint', 'category', 'keyring', 'keyid'], } @@ -311,7 +313,9 @@ class Actions(object): self.output('', '\n Checking keys...') for gkey in sorted(keyresults): self.logger.info("Checking key %s, %s" % (gkey.nick, gkey.keyid)) - self.output('', " %s: %s" % (gkey.name, ', '.join(gkey.keyid))) + self.output('', + "\n %s, %s: %s" % (gkey.nick, gkey.name, ', '.join(gkey.keyid)) + + "\n ==============================================") self.logger.debug("ACTIONS: checkkey; gkey = %s" % str(gkey)) for key in gkey.keyid: results[gkey.name] = self.gpg.check_keys(gkey.keydir, key) @@ -339,6 +343,133 @@ class Actions(object): ]) + def speccheck(self, args): + '''Check keys actions''' + if not args.category: + return (False, ["Please specify seeds type."]) + self.logger.debug("ACTIONS: speccheck; args: %s" % str(args)) + handler = SeedHandler(self.logger, self.config) + seeds = handler.load_category(args.category) + catdir = self.config.get_key(args.category + "-category") + self.logger.debug("ACTIONS: speccheck; catdir = %s" % catdir) + self.gpg = GkeysGPG(self.config, catdir) + results = {} + failed = defaultdict(list) + kwargs = handler.build_gkeydict(args) + keyresults = seeds.list(**kwargs) + self.output('', '\n Checking keys...') + for gkey in sorted(keyresults): + self.logger.info("Checking key %s, %s" % (gkey.nick, gkey.keyid)) + self.output('', + "\n %s, %s: %s" % (gkey.nick, gkey.name, ', '.join(gkey.keyid)) + + "\n ==============================================") + self.logger.debug("ACTIONS: speccheck; gkey = %s" % str(gkey)) + for key in gkey.keyid: + results = self.gpg.speccheck(gkey.keydir, key) + for g in results: + pub_pass = {} + for key in results[g]: + self.output('', key.pretty_print()) + + if key.key is "PUB": + pub_pass = { + 'key': key, + 'pub': key.passed_spec, + 'sign': False, + 'encrypt': False, + 'auth': False, + 'signs': [], + 'encrypts': [], + 'authens': [], + 'final': False, + } + if key.key is "SUB": + if key.sign_capable and key.passed_spec: + pub_pass['signs'].append(key.passed_spec) + pub_pass['sign'] = True + if key.encrypt_capable: + pub_pass['encrypts'].append(key.passed_spec) + pub_pass['encrypt'] = True + if key.capabilities == 'a': + pub_pass['authens'].append(key.passed_spec) + if key.passed_spec: + pub_pass['auth'] = True + validity = key.validity.split(',')[0] + if not key.expire and not 'r' in validity: + failed['expired'].append("%s <%s>: %s" % (gkey.name, gkey.nick, key.fingerprint)) + if 'r' in validity: + failed['revoked'].append("%s <%s>: %s" % (gkey.name, gkey.nick, key.fingerprint)) + if 'i' in validity: + failed['invalid'].append("%s <%s>: %s" % (gkey.name, gkey.nick, key.fingerprint)) + if key.capabilities not in ['a', 'e']: + if not key.algo: + failed['algo'].append("%s <%s>: %s" % (gkey.name, gkey.nick, key.fingerprint)) + if not key.bits: + failed['bits'].append("%s <%s>: %s" % (gkey.name, gkey.nick, key.fingerprint)) + if "Warning" in key.expire_reason: + failed['warn'].append("%s <%s>: %s " % (gkey.name, gkey.nick, key.fingerprint)) + if True in pub_pass['signs']: + pub_pass['sign'] = True + if True in pub_pass['encrypts']: + pub_pass['encrypt'] = True + if not pub_pass['sign']: + failed['sign'].append("%s <%s>: %s" % (gkey.name, gkey.nick, pub_pass['key'].fingerprint)) + if not pub_pass['encrypt']: + failed['encrypt'].append("%s <%s>: %s" % (gkey.name, gkey.nick, pub_pass['key'].fingerprint)) + spec = "%s <%s>: %s" % (gkey.name, gkey.nick, pub_pass['key'].fingerprint) + for k in ['pub', 'sign']: + if pub_pass[k]: + pub_pass['final'] = True + else: + pub_pass['final'] = False + break + if pub_pass['final']: + if spec not in failed['spec-approved']: + failed['spec-approved'].append(spec) + else: + if spec not in failed['spec']: + failed['spec'].append(spec) + sdata = convert_pf(pub_pass, ['pub', 'sign', 'final']) + sdata = convert_yn(sdata, ['auth', 'encrypt']) + self.output('', SPECCHECK_SUMMARY % sdata) + + if failed['revoked']: + self.output([sorted(set(failed['revoked']))], '\n Revoked keys:') + if failed['invalid']: + self.output([sorted(set(failed['invalid']))], '\n Invalid keys:') + if failed['sign']: + self.output([sorted(set(failed['sign']))], '\n No signing capable subkey:') + if failed['encrypt']: + self.output([sorted(set(failed['encrypt']))], '\n No Encryption capable subkey (Notice only):') + if failed['algo']: + self.output([sorted(set(failed['algo']))], '\n Incorrect Algorithm:') + if failed['bits']: + self.output([sorted(set(failed['bits']))], '\n Incorrect bit length:') + if failed['expired']: + self.output([sorted(set(failed['expired']))], '\n Expiry keys:') + if failed['warn']: + self.output([sorted(set(failed['warn']))], '\n Expiry Warnings:') + if failed['spec']: + self.output([sorted(set(failed['spec']))], '\n Failed to pass SPEC requirements:') + if failed['spec-approved']: + self.output([sorted(set(failed['spec-approved']))], '\n SPEC Approved:') + + return (len(failed) <1, + ['\nFound Failures:\n-------', + 'Revoked................: %d' % len(set(failed['revoked'])), + 'Invalid................: %d' % len(set(failed['invalid'])), + 'No Signing subkey......: %d' % len(set(failed['sign'])), + 'No Encryption subkey...: %d' % len(set(failed['encrypt'])), + 'Algorithm..............: %d' % len(set(failed['algo'])), + 'Bit length.............: %d' % len(set(failed['bits'])), + 'Expiry.................: %d' % len(set(failed['expired'])), + 'Expiry Warnings........: %d' % len(set(failed['warn'])), + 'SPEC requirements......: %d' % len(set(failed['spec'])), + '=============================', + 'SPEC Approved..........: %d' % len(set(failed['spec-approved'])), + ]) + + def removekey(self, args): '''Remove an installed key''' if not args.nick: diff --git a/gkeys/checks.py b/gkeys/checks.py index 2d4be4c..db3d59f 100644 --- a/gkeys/checks.py +++ b/gkeys/checks.py @@ -9,42 +9,172 @@ @license: GNU GPL2, see COPYING for details """ +import time +from collections import namedtuple, OrderedDict from gkeys.config import GKEY_CHECK +from pyGPG.mappings import (ALGORITHM_CODES, CAPABILITY_MAP, + KEY_VERSION_FPR_LEN, VALIDITY_MAP, INVALID_LIST, + VALID_LIST) + + +SPEC_INDEX = { + 'key': 0, + 'capabilities': 1, + 'fingerprint': 2, + 'bits': 3, + 'created': 4, + 'expire': 5, + 'encrypt_capable': 6, + 'sign_capable': 7, + 'algo': 8, + 'version': 9, + 'id': 10, + 'days': 11, + 'validity': 12, + 'expire_reason': 13, + 'long_caps': 14, # long version of the capbilities + 'caps': 15, + 'caps_reason': 16, + 'id_reason': 17, + 'is_valid': 18, + 'passed_spec': 19, +} + +SPEC_INDEX = OrderedDict(sorted(SPEC_INDEX.items(), key=lambda t: t[1])) + +SPEC_STAT = ['', '','', False, False, False, False, False, False, False, False, + 0, '', '', '', True, '', '', False, False] # Default glep 63 minimum gpg key specification +# and approved options, limits TEST_SPEC = { 'bits': { 'DSA': 2048, 'RSA': 2048, }, - 'expire': 36, # in months + 'expire': 3 * 365, # in days 'subkeys': { # warning/error mode - 'encryption': { + 'encrypt': { 'mode': 'notice', - 'expire': -1, # -1 is the primary key expirery + 'expire': 3 * 365, }, 'sign': { 'mode': 'error', - 'expire': 12, + 'expire': 365, }, }, - 'type': ['DSA', 'RSA'], - 'version': 4, + 'algorithms': ['DSA', 'RSA', '1', '2', '3', '17'], + 'versions': ['4'], + 'qualified_id': '@gentoo.org', } +# Final pass/fail fields and the pass value required +TEST_REQUIREMENTS = { + 'bits': True, + 'created': True, + 'expire': True, + 'sign_capable': True, + 'algo': True, + 'version': True, + 'id': True, + 'is_valid': True, + 'caps': True, +} + +SECONDS_PER_DAY = 86400 + + +SPECCHECK_STRING = ''' ---------- + Fingerprint......: %(fingerprint)s + Key type ........: %(key)s Capabilities.: %(capabilities)s %(long_caps)s + Algorithm........: %(algo)s Bit Length...: %(bits)s + Create Date......: %(created)s Expire Date..: %(expire)s + Key Version......: %(version)s Validity.....: %(validity)s + Days till expiry.: %(days)s %(expire_reason)s + Capability.......: %(caps)s %(caps_reason)s + Qualified ID.....: %(id)s %(id_reason)s + This %(pub_sub)s.: %(passed_spec)s''' + +SPECCHECK_SUMMARY = ''' Key summary + primary..........: %(pub)s signing subkey: %(sign)s + encryption subkey: %(encrypt)s authentication subkey: %(auth)s + SPEC requirements: %(final)s +''' + +def convert_pf(data, fields): + '''Converts dictionary items from True/False to Pass/Fail strings + + @param data: dict + @param fields: list + @returns: dict + ''' + for f in fields: + if data[f]: + data[f] = 'Pass' + else: + data[f] = 'Fail' + return data + +def convert_yn(data, fields): + '''Converts dictionary items from True/False to Yes/No strings + + @param data: dict + @param fields: list + @returns: dict + ''' + for f in fields: + if data[f]: + data[f] = 'Yes ' + else: + data[f] = 'No ' + return data + + +class SpecCheck(namedtuple("SpecKey", list(SPEC_INDEX))): + + __slots__ = () + + def pretty_print(self): + data = self.convert_data() + output = SPECCHECK_STRING % (data) + return output + + + def convert_data(self): + data = dict(self._asdict()) + data = convert_pf(data, ['algo', 'bits', 'caps', 'created', 'expire', 'id', + 'passed_spec', 'version']) + for f in ['caps', 'id']: + data[f] = data[f].ljust(10) + data['validity'] += ', %s' % (VALIDITY_MAP[data['validity']]) + days = data['days'] + if days == float("inf"): + data['days'] = "infinite".ljust(10) + else: + data['days'] = str(int(data['days'])).ljust(10) + if data['capabilities'] == 'e': + data['algo'] = '----' + data['bits'] = '----' + if data['key'] =='PUB': + data['pub_sub'] = 'primary key' + else: + data['pub_sub'] = 'subkey.....' + return data + class KeyChecks(object): - '''Primary gpg key validation and glep spec checks class''' + '''Primary gpg key validation and specifications checks class''' - def __init__(self, logger, spec=TEST_SPEC): + def __init__(self, logger, spec=TEST_SPEC, qualified_id_check=True): '''@param spec: optional gpg specification to test against Defaults to TEST_SPEC ''' self.logger = logger self.spec = spec + self.check_id = qualified_id_check def validity_checks(self, keydir, keyid, result): @@ -97,8 +227,211 @@ class KeyChecks(object): return GKEY_CHECK(keyid, revoked, expired, invalid, sign) - def glep_check(self, keydir, keyid, result): + def spec_check(self, keydir, keyid, result): '''Performs the minimum specifications checks on the key''' - pass + self.logger.debug("SPEC_CHECK() : CHECKING: %s" % keyid) + results = {} + pub = None + stats = None + pub_days = 0 + for data in result.status.data: + if data.name == "PUB": + if stats: + stats = self._test_final(data, stats) + results[pub.long_keyid].append(SpecCheck._make(stats)) + pub = data + found_id = False + found_id_reason = '' + results[data.long_keyid] = [] + stats = SPEC_STAT[:] + stats[SPEC_INDEX['key']] = data.name + stats[SPEC_INDEX['capabilities']] = data.key_capabilities + stats[SPEC_INDEX['validity']] = data.validity + stats = self._test_created(data, stats) + stats = self._test_algo(data, stats) + stats = self._test_bits(data, stats) + stats = self._test_expire(data, stats, pub_days) + pub_days = stats[SPEC_INDEX['days']] + stats = self._test_caps(data, stats) + stats = self._test_validity(data, stats) + elif data.name == "FPR": + pub = pub._replace(**{'fingerprint': data.fingerprint}) + stats[SPEC_INDEX['fingerprint']] = data.fingerprint + stats = self._test_version(data, stats) + elif data.name == "UID": + stats = self._test_uid(data, stats) + if stats[SPEC_INDEX['id']] in [True, '-----']: + found_id = stats[SPEC_INDEX['id']] + found_id_reason = '' + stats[SPEC_INDEX['id_reason']] = '' + else: + found_id_reason = stats[SPEC_INDEX['id_reason']] + elif data.name == "SUB": + if stats: + stats = self._test_final(data, stats) + results[pub.long_keyid].append(SpecCheck._make(stats)) + stats = SPEC_STAT[:] + stats[SPEC_INDEX['key']] = data.name + stats[SPEC_INDEX['capabilities']] = data.key_capabilities + stats[SPEC_INDEX['fingerprint']] = '%s' \ + % (data.long_keyid) + stats[SPEC_INDEX['id']] = found_id + stats[SPEC_INDEX['id_reason']] = found_id_reason + stats[SPEC_INDEX['validity']] = data.validity + stats = self._test_validity(data, stats) + stats = self._test_created(data, stats) + stats = self._test_algo(data, stats) + stats = self._test_bits(data, stats) + stats = self._test_expire(data, stats, pub_days) + stats = self._test_caps(data, stats) + if stats: + stats = self._test_final(data, stats) + results[pub.long_keyid].append(SpecCheck._make(stats)) + stats = None + self.logger.debug("SPEC_CHECK() : COMPLETED: %s" % keyid) + return results + + + def _test_algo(self, data, stats): + algo = data.pubkey_algo + if algo in TEST_SPEC['algorithms']: + stats[SPEC_INDEX['algo']] = True + else: + self.logger.debug("ERROR in key %s : invalid Type: %s" + % (data.long_keyid, ALGORITHM_CODES[algo])) + return stats + + + def _test_bits(self, data, stats): + bits = int(data.keylength) + if data.pubkey_algo in TEST_SPEC['algorithms']: + if bits >= TEST_SPEC['bits'][ALGORITHM_CODES[data.pubkey_algo]]: + stats[SPEC_INDEX['bits']] = True + else: + self.logger.debug("ERROR in key %s : invalid Bit length: %d" + % (data.long_keyid, bits)) + return stats + + + def _test_version(self, data, stats): + fpr_l = len(data.fingerprint) + if KEY_VERSION_FPR_LEN[fpr_l] in TEST_SPEC['versions']: + stats[SPEC_INDEX['version']] = True + else: + self.logger.debug("ERROR in key %s : invalid gpg key version: %s" + % (data.long_keyid, KEY_VERSION_FPR_LEN[fpr_l])) + return stats + + + def _test_created(self, data, stats): + try: + created = float(data.creation_date) + except ValueError: + created = 0 + if created <= time.time() : + stats[SPEC_INDEX['created']] = True + else: + self.logger.debug("ERROR in key %s : invalid gpg key creation date: %s" + % (data.long_keyid, data.creation_date)) + return stats + + + def _test_expire(self, data, stats, pub_days): + if data.name in ["PUB"]: + delta_t = TEST_SPEC['expire'] + stats = self._expire_check(data, stats, delta_t, pub_days) + return stats + else: + for cap in data.key_capabilities: + try: + delta_t = TEST_SPEC['subkeys'][CAPABILITY_MAP[cap]]['expire'] + except KeyError: + self.logger.debug( + "WARNING in capability key %s : setting delta_t to main expiry: %d" + % (cap, TEST_SPEC['expire'])) + delta_t = TEST_SPEC['expire'] + stats = self._expire_check(data, stats, delta_t, pub_days) + return stats + + + def _expire_check(self, data, stats, delta_t, pub_days): + today = time.time() + try: + expires = float(data.expiredate) + except ValueError: + expires = float("inf") + if data.name == 'SUB' and expires == float("inf"): + days = stats[SPEC_INDEX['days']] = pub_days + elif expires == float("inf"): + days = stats[SPEC_INDEX['days']] = expires + else: + days = stats[SPEC_INDEX['days']] = max(0, int((expires - today)/SECONDS_PER_DAY)) + if days <= delta_t: + stats[SPEC_INDEX['expire']] = True + elif days > delta_t and not ('i' in data.validity or 'r' in data.validity): + stats[SPEC_INDEX['expire_reason']] = '<== Exceeds specification' + else: + self.logger.debug("ERROR in key %s : invalid gpg key expire date: %s" + % (data.long_keyid, data.expiredate)) + if 0 < days < 30 and not ('i' in data.validity or 'r' in data.validity): + stats[SPEC_INDEX['expire_reason']] = '<== WARNING < 30 days' + + return stats + + + def _test_caps(self, data, stats): + if 'e' in data.key_capabilities: + if 's' in data.key_capabilities or 'a' in data.key_capabilities: + stats[SPEC_INDEX['caps']] = False + stats[SPEC_INDEX['caps_reason']] = "<== Mixing of 'e' with 's' and/or 'a'" + if not stats[SPEC_INDEX['is_valid']]: + return stats + kcaps = [] + for cap in data.key_capabilities: + if CAPABILITY_MAP[cap] and stats[SPEC_INDEX['caps']]: + kcaps.append(CAPABILITY_MAP[cap]) + if cap in ["s"] and not data.name == "PUB": + stats[SPEC_INDEX['sign_capable']] = True + elif cap in ["e"]: + stats[SPEC_INDEX['encrypt_capable']] = True + elif cap not in CAPABILITY_MAP: + stats[SPEC_INDEX['caps']] = False + self.logger.debug("ERROR in key %s : unknown gpg key capability: %s" + % (data.long_keyid, cap)) + stats[SPEC_INDEX['long_caps']] = ', '.join(kcaps) + return stats + + + def _test_uid(self, data, stats): + if not self.check_id: + stats[SPEC_INDEX['id']] = '-----' + stats[SPEC_INDEX['id_reason']] = '' + return stats + if TEST_SPEC['qualified_id'] in data.user_ID : + stats[SPEC_INDEX['id']] = True + stats[SPEC_INDEX['id_reason']] = '' + else: + stats[SPEC_INDEX['id_reason']] = "<== '%s' user id not found" % TEST_SPEC['qualified_id'] + self.logger.debug("Warning: No qualified ID found in key %s" + % (data.user_ID)) + return stats + + + def _test_validity(self, data, stats): + if data.validity in VALID_LIST: + stats[SPEC_INDEX['is_valid']] = True + return stats + def _test_final(self, data, stats): + for test, result in TEST_REQUIREMENTS.items(): + if ((stats[SPEC_INDEX['key']] == 'PUB' and test == 'sign_capable') or + (stats[SPEC_INDEX['capabilities']] == 'e' and test in ['algo', 'bits', 'sign_capable']) + or (stats[SPEC_INDEX['capabilities']] == 'a' and test in ['sign_capable'])): + continue + if stats[SPEC_INDEX[test]] == result: + stats[SPEC_INDEX['passed_spec']] = True + else: + stats[SPEC_INDEX['passed_spec']] = False + break + return stats diff --git a/gkeys/lib.py b/gkeys/lib.py index c27f85d..d2b3119 100644 --- a/gkeys/lib.py +++ b/gkeys/lib.py @@ -206,7 +206,7 @@ class GkeysGPG(GPG): task = 'list-keys' target = keydir self.set_keydir(keydir, task, fingerprint=True) - self.config.options['tasks'][task].extend(['--keyid-format', 'long']) + self.config.options['tasks'][task].extend(['--keyid-format', 'long', '--with-fingerprint']) if colons: task_value = ['--with-colons'] self.config.options['tasks'][task].extend(task_value) @@ -228,10 +228,26 @@ class GkeysGPG(GPG): ''' if not result: result = self.list_keys(keydir, fingerprint=keyid, colons=True) - checker = KeyChecks(logger) + checker = KeyChecks(logger, qualified_id_check=True) return checker.validity_checks(keydir, keyid, result) + def speccheck(self, keydir, keyid, result=None): + '''Check specified or all keys based on the seed type + specifications are met. + + @param keydir: the keydir to list the keys for + @param keyid: the keyid to check + @param result: optional pyGPG.output.GPGResult object + @returns: SpecCheck instance + ''' + if not result: + result = self.list_keys(keydir, fingerprint=keyid, colons=True) + checker = KeyChecks(logger, qualified_id_check=True) + specchecks = checker.spec_check(keydir, keyid, result) + return specchecks + + def list_keydirs(self): '''List all available keydirs '''