paycheck: unit tests + fixes to checker module

This adds missing unit tests for the checker module, bundled with fixes
to some bugs that surfaced due to unit tests. This includes:

* A fake extent (signified by start_block == UINT64_MAX) that
  accompanies a signature data blob bears different requirements than
  previously implemented.  Specifically, the extent sequence must have
  exactly one extent; and the number of blocks is not necessarily one,
  rather it is the correct number that corresponds to the actual length
  of the signature blob.

* REPLACE/REPLACE_BZ operations must contain data.

* MOVE operation validation must ensure that all of the actual message
  extents are being used.

* BSDIFF operation must contain data (the diff).

* Signature pseudo-operation should be a REPLACE.

BUG=chromium-os:34911,chromium-os:33607,chromium-os:7597
TEST=Passes unittests (upcoming); works with actual payloads.

Change-Id: I4d839d1d4da1fbb4a493b208958a139368e2c8ca
Reviewed-on: https://gerrit.chromium.org/gerrit/45429
Tested-by: Gilad Arnold <[email protected]>
Reviewed-by: Chris Sosa <[email protected]>
Commit-Queue: Gilad Arnold <[email protected]>
diff --git a/scripts/update_payload/common.py b/scripts/update_payload/common.py
index 5e0087b..6b5dbad 100644
--- a/scripts/update_payload/common.py
+++ b/scripts/update_payload/common.py
@@ -15,6 +15,12 @@
 #
 PSEUDO_EXTENT_MARKER = ctypes.c_uint64(-1).value
 
+SIG_ASN1_HEADER = (
+    '\x30\x31\x30\x0d\x06\x09\x60\x86'
+    '\x48\x01\x65\x03\x04\x02\x01\x05'
+    '\x00\x04\x20'
+)
+
 
 #
 # Payload operation types.
@@ -27,6 +33,7 @@
   REPLACE_BZ = _CLASS.REPLACE_BZ
   MOVE = _CLASS.MOVE
   BSDIFF = _CLASS.BSDIFF
+  ALL = (REPLACE, REPLACE_BZ, MOVE, BSDIFF)
   NAMES = {
       REPLACE: 'REPLACE',
       REPLACE_BZ: 'REPLACE_BZ',
@@ -41,6 +48,39 @@
 #
 # Checker and hashed reading of data.
 #
+def IntPackingFmtStr(size, is_unsigned):
+  """Returns an integer format string for use by the struct module.
+
+  Args:
+    size: the integer size in bytes (2, 4 or 8)
+    is_unsigned: whether it is signed or not
+  Returns:
+    A format string for packing/unpacking integer values; assumes network byte
+    order (big-endian).
+  Raises:
+    PayloadError if something is wrong with the arguments.
+
+  """
+  # Determine the base conversion format.
+  if size == 2:
+    fmt = 'h'
+  elif size == 4:
+    fmt = 'i'
+  elif size == 8:
+    fmt = 'q'
+  else:
+    raise PayloadError('unsupport numeric field size (%s)' % size)
+
+  # Signed or unsigned?
+  if is_unsigned:
+    fmt = fmt.upper()
+
+  # Make it network byte order (big-endian).
+  fmt = '!' + fmt
+
+  return fmt
+
+
 def Read(file_obj, length, offset=None, hasher=None):
   """Reads binary data from a file.