gpg-migrate.git
11 years agoAdd public key parsing to PGPPacket
W. Trevor King [Thu, 19 Dec 2013 05:52:42 +0000 (21:52 -0800)]
Add public key parsing to PGPPacket

Use the same parser for public-key and public-subkey packets.  From
RFC 4880 [1]:

  A Public-Subkey packet (tag 14) has exactly the same format as a
  Public-Key packet, but denotes a subkey.  One or more subkeys may be
  associated with a top-level key.

The generic (sub)key parsing is specified in section 5.5.2 [2]:

  OpenPGP implementations MUST create keys with version 4 format.  V3
  keys are deprecated; an implementation MUST NOT generate a V3 key,
  but MAY accept it.

  ...

  A version 4 packet contains:

  - A one-octet version number (4).
  - A four-octet number denoting the time that the key was created.
  - A one-octet number denoting the public-key algorithm of this key.
  - A series of multiprecision integers comprising the key material.

Also check that the --export packets begin with a public-key packet.
From RFC 4880 [3]:

  A Public-Key packet starts a series of packets that forms an OpenPGP
  key (sometimes called an OpenPGP certificate).

[1]: http://tools.ietf.org/search/rfc4880#section-5.5.1.2
[2]: http://tools.ietf.org/search/rfc4880#section-5.5.2
[3]: http://tools.ietf.org/search/rfc4880#section-5.5.1.1

11 years agoAdd a flexible packet-parsing framework
W. Trevor King [Thu, 19 Dec 2013 05:09:42 +0000 (21:09 -0800)]
Add a flexible packet-parsing framework

Shunting packet-type processing out to type-specific methods.  For
example, 'public-key packet' packets will be parsed by
PGPPacket._parse_public_key_packet.

From the re docs [1]:

  '+'
    Causes the resulting RE to match 1 or more repetitions of the
    preceding RE. ab+ will match 'a' followed by any non-zero number
    of 'b's; it will not match just 'a'.

  ...

  \W
    For Unicode (str) patterns:
      Matches Unicode word characters; this includes most characters
      that can be part of a word in any language, as well as numbers
      and the underscore. If the ASCII flag is used, only [a-zA-Z0-9_]
      is matched (but the flag affects the entire regular expression,
      so in such cases using an explicit [a-zA-Z0-9_] may be a better
      choice).

    For 8-bit (bytes) patterns:
      Matches characters considered alphanumeric in the ASCII
      character set; this is equivalent to [a-zA-Z0-9_].

[1]: http://docs.python.org/3/library/re.html#regular-expression-syntax

11 years agogpg-migrate.py: Convert PGPPacket 'packet-tag' key to 'type'
W. Trevor King [Thu, 19 Dec 2013 04:56:42 +0000 (20:56 -0800)]
gpg-migrate.py: Convert PGPPacket 'packet-tag' key to 'type'

And convert the integer to a string using the table from RFC 4880 [1]:

  The packet tag denotes what type of packet the body holds.  Note
  that old format headers can only have tags less than 16, whereas new
  format headers can have tags as great as 63.  The defined tags (in
  decimal) are as follows:

       0        -- Reserved - a packet tag MUST NOT have this value
       1        -- Public-Key Encrypted Session Key Packet
       2        -- Signature Packet
       3        -- Symmetric-Key Encrypted Session Key Packet
       4        -- One-Pass Signature Packet
       5        -- Secret-Key Packet
       6        -- Public-Key Packet
       7        -- Secret-Subkey Packet
       8        -- Compressed Data Packet
       9        -- Symmetrically Encrypted Data Packet
       10       -- Marker Packet
       11       -- Literal Data Packet
       12       -- Trust Packet
       13       -- User ID Packet
       14       -- Public-Subkey Packet
       17       -- User Attribute Packet
       18       -- Sym. Encrypted and Integrity Protected Data Packet
       19       -- Modification Detection Code Packet
       60 to 63 -- Private or Experimental Values

[1]: http://tools.ietf.org/search/rfc4880#section-4.3

11 years agoPull PGPPacket header parsing out into _parse_header
W. Trevor King [Thu, 19 Dec 2013 04:46:48 +0000 (20:46 -0800)]
Pull PGPPacket header parsing out into _parse_header

This parsing is distinct from the payload parsing, so stash it in a
separate method.

11 years agoAdd packets_from_bytes to help parse multi-packet streams
W. Trevor King [Thu, 19 Dec 2013 04:45:21 +0000 (20:45 -0800)]
Add packets_from_bytes to help parse multi-packet streams

11 years agoCheck for short packets in PGPPacket.from_bytes
W. Trevor King [Thu, 19 Dec 2013 04:44:30 +0000 (20:44 -0800)]
Check for short packets in PGPPacket.from_bytes

When the data is longer than expected, the packet may just be part of
a stream.

11 years agoTrack data offset in bytes, and extract the packet payload
W. Trevor King [Thu, 19 Dec 2013 04:37:33 +0000 (20:37 -0800)]
Track data offset in bytes, and extract the packet payload

Return the offset when we're done, because there may be multiple
packets in a stream.

11 years agoExtract the packet length from the old-format length header
W. Trevor King [Thu, 19 Dec 2013 04:31:15 +0000 (20:31 -0800)]
Extract the packet length from the old-format length header

From RFC 4880 [1]:

  Scalar numbers are unsigned and are always stored in big-endian
  format.  Using n[k] to refer to the kth octet being interpreted, the
  value of a two-octet scalar is ((n[0] << 8) + n[1]).  The value of a
  four-octet scalar is ((n[0] << 24) + (n[1] << 16) + (n[2] << 8) +
  n[3]).

The struct big-endian byte-order character is '>' [2].

[1]: http://tools.ietf.org/search/rfc4880#section-3.1
[2]: http://docs.python.org/3/library/struct.html#byte-order-size-and-alignment

11 years agoAdd PGPPacket._old_format_packet_lengths
W. Trevor King [Thu, 19 Dec 2013 04:05:25 +0000 (20:05 -0800)]
Add PGPPacket._old_format_packet_lengths

From RFC 4880 [1]:

   The meaning of the length-type in old format packets is:

   0 - The packet has a one-octet length.  The header is 2 octets long.

   1 - The packet has a two-octet length.  The header is 3 octets long.

   2 - The packet has a four-octet length.  The header is 5 octets long.

   3 - The packet is of indeterminate length.  The header is 1 octet
       long, and the implementation must determine how long the packet
       is.  If the packet is in a file, this means that the packet
       extends until the end of the file.  In general, an implementation
       SHOULD NOT use indeterminate-length packets except where the end
       of the data will be clear from the context, and even then it is
       better to use a definite length, or a new format header.  The new
       format headers described below have a mechanism for precisely
       encoding data of indeterminate length.

The struct format characters are [2]:

  Format   C Type           Python type   Standard size   Notes
  B        unsigned char    integer       1               (3)
  H        unsigned short   integer       2               (3)
  I        unsigned int     integer       4               (3)

  3. When attempting to pack a non-integer using any of the integer
     conversion codes, if the non-integer has a __index__() method
     then that method is called to convert the argument to an integer
     before packing.

     Changed in version 3.2: Use of the __index__() method for
     non-integers is new in 3.2.

[1]: http://tools.ietf.org/search/rfc4880#section-4.2.1
[2]: http://docs.python.org/3/library/struct.html#format-characters

11 years agoExtract the packet tag
W. Trevor King [Thu, 19 Dec 2013 03:51:10 +0000 (19:51 -0800)]
Extract the packet tag

From RFC 4880 [1]:

  The first octet of the packet header is called the "Packet Tag".  It
  determines the format of the header and denotes the packet contents.
  The remainder of the packet header is the length of the packet.

  Note that the most significant bit is the leftmost bit, called bit
  7.  A mask for this bit is 0x80 in hexadecimal.

              +---------------+
         PTag |7 6 5 4 3 2 1 0|
              +---------------+
         Bit 7 -- Always one
         Bit 6 -- New packet format if set

  PGP 2.6.x only uses old format packets.  Thus, software that
  interoperates with those versions of PGP must only use old format
  packets.  If interoperability is not an issue, the new packet format
  is RECOMMENDED.  Note that old format packets have four bits of
  packet tags, and new format packets have six; some features cannot
  be used and still be backward-compatible.

  Also note that packets with a tag greater than or equal to 16 MUST
  use new format packets.  The old format packets can only express
  tags less than or equal to 15.

  Old format packets contain:

         Bits 5-2 -- packet tag
         Bits 1-0 -- length-type

  New format packets contain:

         Bits 5-0 -- packet tag

[1]: http://tools.ietf.org/search/rfc4880#section-4.2

11 years agoStub out gpg-migrate.py
W. Trevor King [Thu, 19 Dec 2013 03:49:08 +0000 (19:49 -0800)]
Stub out gpg-migrate.py

Following the general approach outlined by Atom Smasher [1], but I'll
just parse the packets directly in Python.

[1]: http://atom.smasher.org/gpg/gpg-migrate.txt