Commits

Tobias Mueller committed 9887552

Initial implementation of a gpg --with-colons parser

Comments (0)

Files changed (1)

+#!/usr/bin/env python
+'''A very simple module containing definitions of GPG's --with-colons format'''
+
+from collections import namedtuple
+import logging
+
+log = logging.getLogger(__name__)
+
+fields_explanation = [
+{'type': '''1. Field:  Type of record
+            pub = public key
+            crt = X.509 certificate
+            crs = X.509 certificate and private key available
+            sub = subkey (secondary key)
+            sec = secret key
+            ssb = secret subkey (secondary key)
+            uid = user id (only field 10 is used).
+            uat = user attribute (same as user id except for field 10).
+            sig = signature
+            rev = revocation signature
+            fpr = fingerprint: (fingerprint is in field 10)
+            pkd = public key data (special field format, see below)
+            grp = reserved for gpgsm
+            rvk = revocation key
+            tru = trust database information
+            spk = signature subpacket'''},
+    
+{'trust': '''2. Field:  A letter describing the calculated trust. This is a single
+            letter, but be prepared that additional information may follow
+            in some future versions. (not used for secret keys)
+                o = Unknown (this key is new to the system)
+                i = The key is invalid (e.g. due to a missing self-signature)
+                d = The key has been disabled
+                    (deprecated - use the 'D' in field 12 instead)
+                r = The key has been revoked
+                e = The key has expired
+                - = Unknown trust (i.e. no value assigned)
+                q = Undefined trust
+                    '-' and 'q' may safely be treated as the same
+                    value for most purposes
+                n = Don't trust this key at all
+                m = There is marginal trust in this key
+                f = The key is fully trusted
+                u = The key is ultimately trusted.  This often means
+                    that the secret key is available, but any key may
+                    be marked as ultimately trusted.'''},
+                    
+{'keylen': '3. Field:  length of key in bits.'},
+
+{'algorithm': ''' 4. Field:  Algorithm:  1 = RSA
+                       16 = Elgamal (encrypt only)
+                       17 = DSA (sometimes called DH, sign only)
+                       20 = Elgamal (sign and encrypt - don't use them!)                       
+            (for other id's see include/cipher.h)'''},
+            
+{'keyid': '5. Field:  KeyID'},
+
+{'creation': '''6. Field:  Creation Date (in UTC).  For UID and UAT records, this is the
+            self-signature date.  Note that the dae is usally printed
+            in seconds since epoch, however, we are migrating to an ISO
+            8601 format (e.g. "19660205T091500").  This is currently
+            only relevant for X.509, A simple way to detect the format
+            is be scannning for the 'T'.'''},
+            
+{'expiry': '''7. Field:  Key or user ID/user attribute expiration date or empty if none.'''},
+
+{'serial': '''8. Field:  Used for serial number in crt records (used to be the Local-ID).
+            For UID and UAT records, this is a hash of the user ID contents
+            used to represent that exact user ID.  For trust signatures,
+            this is the trust depth seperated by the trust value by a
+            space.'''},
+
+{'ownertrust': '''9. Field:  Ownertrust (primary public keys only)
+            This is a single letter, but be prepared that additional
+            information may follow in some future versions.  For trust
+            signatures with a regular expression, this is the regular
+            expression value, quoted as in field 10.'''},
+
+{'userid': '''10. Field:  User-ID.  The value is quoted like a C string to avoid
+            control characters (the colon is quoted "\x3a").
+            This is not used with --fixed-list-mode in gpg.
+            A UAT record puts the attribute subpacket count here, a
+            space, and then the total attribute subpacket size.
+            In gpgsm the issuer name comes here
+            An FPR record stores the fingerprint here.
+            The fingerprint of an revocation key is stored here.'''},
+
+{'sig_class': '''11. Field:  Signature class.  This is a 2 digit hexnumber followed by
+            either the letter 'x' for an exportable signature or the
+            letter 'l' for a local-only signature.
+            The class byte of an revocation key is also given here,
+            'x' and 'l' ist used the same way.'''},
+
+{'key_caps': '''12. Field:  Key capabilities:
+                e = encrypt
+                s = sign
+                c = certify
+                a = authentication
+            A key may have any combination of them in any order.  In
+            addition to these letters, the primary key has uppercase
+            versions of the letters to denote the _usable_
+            capabilities of the entire key, and a potential letter 'D'
+            to indicate a disabled key.'''},
+
+{'sig_issuer': '''13. Field:  Used in FPR records for S/MIME keys to store the fingerprint of
+            the issuer certificate.  This is useful to build the
+            certificate path based on certificates stored in the local
+            keyDB; it is only filled if the issue certificate is
+            available. The advantage of using this value is that it is
+            guaranteed to have been been build by the same lookup
+            algorithm as gpgsm uses.
+            For "uid" recods this lists the preferences n the sameway the 
+            -edit menu does.
+            For "sig" records, this is the fingerprint of the key that
+            issued the signature.  Note that this is only filled in if
+            the signature verified correctly.  Note also that for
+            various technical reasons, this fingerprint is only
+            available if --no-sig-cache is used.'''},
+
+{'edit': '14. Field   Flag field used in the --edit menu output:'},
+
+{'token': '''15. Field   Used in sec/sbb to print the serial number of a token
+            (internal protect mode 1002) or a '#' if that key is a
+            simple stub (internal protect mode 1001)'''},
+]
+
+
+types_of_record = (
+            'pub', #public key
+            'crt', #X.509 certificate
+            'crs', #X.509 certificate and private key available
+            'sub', #subkey (secondary key)
+            'sec', #secret key
+            'ssb', #secret subkey (secondary key)
+            'uid', #user id (only field 10 is used).
+            'uat', #user attribute (same as user id except for field 10).
+            'sig', #signature
+            'rev', #revocation signature
+            'fpr', #fingerprint: (fingerprint is in field 10)
+            'pkd', #public key data (special field format, see below)
+            'grp', #reserved for gpgsm
+            'rvk', #revocation key
+            'tru', #trust database information
+            'spk', #signature subpacket
+)
+
+fields = [k.keys()[0] for k in fields_explanation]
+
+_GPGRecordLine = namedtuple('GPGRecordLine', fields)
+'''This class is meant to hold gpg records that gpg --with-colons 
+produces, i.e.
+
+sig:::1:965089CE6B95F882:2012-04-05::::Carlos Alberto Lopez Perez <clopez@igalia.com>:10x:
+
+however, the format documented in doc/DETAILS lists 16 fields whereas 
+the signature records seem to have 13 fields only. It is unclear for 
+now which fields are missing.
+'''
+
+
+class GPGRecordLine(_GPGRecordLine):
+    '''This is a tuple but can handle fewer args than needed by automatically filling up the missing values.
+    That's done quite hackily but it seems to work. Improvements are welcome.
+    '''
+    def __new__(_cls, *args, **kwargs):
+        field_len = len(_GPGRecordLine._fields)
+        args_len = len(args)
+        if  args_len < field_len:
+            needed_elements = field_len - args_len
+            additional_args = ['' for e in xrange(needed_elements)]
+            normalised_args = list(args) + additional_args
+        else:
+            normalised_args = args
+
+        return _GPGRecordLine(*normalised_args, **kwargs)
+        
+    @classmethod
+    def many_from_colon_lines(cls, lines, filter=types_of_record):
+        '''Generates a new RecordLine from a given gpg --with-colons output.
+        
+        You filter for some packet types by specifying the filter argument.
+        By default, it holds the list of all possible packet type which is
+        defined by "type_of_record".
+        '''
+        for line in lines.splitlines():
+            fields = line.split(':')
+            
+            filtered_lines = filter
+            if not fields[0] in filtered_lines:
+                log.debug('Parsing %s which is not a %s',
+                    fields, filtered_lines)
+            else:
+                record_line = GPGRecordLine(*fields)
+                log.debug("Parsed into the following: %s", record_line)
+                yield record_line
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.