Add support for registration codes.

BUG=chrome-os-partner:11912
TEST=registration_codes_unittest.py, manual end-to-end test

Change-Id: Ib5729a44952439e90a0d79e1d87484e5593a9ed3
Reviewed-on: https://gerrit.chromium.org/gerrit/28683
Tested-by: Jon Salz <jsalz@chromium.org>
Reviewed-by: Hung-Te Lin <hungte@chromium.org>
Commit-Ready: Jon Salz <jsalz@chromium.org>
diff --git a/Makefile b/Makefile
index d0d7abd..4a67ae2 100644
--- a/Makefile
+++ b/Makefile
@@ -65,6 +65,7 @@
 	py/goofy/updater_unittest.py \
 	py/test/factory_unittest.py \
 	py/test/state_unittest.py \
+	py/test/registration_codes_unittest.py \
 	py/test/unicode_to_string_unittest.py \
 	py/test/utils_unittest.py \
 	py/utils/net_utils_unittest.py \
diff --git a/py/test/registration_codes.py b/py/test/registration_codes.py
new file mode 100644
index 0000000..4d48d70
--- /dev/null
+++ b/py/test/registration_codes.py
@@ -0,0 +1,26 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import binascii
+import re
+import struct
+
+
+def CheckRegistrationCode(code):
+  '''Checks that a registration code is valid, raising a ValueError if not.'''
+  if len(code) != 72:
+    raise ValueError('Registration code %r is not 72 characters long' % code)
+  if re.search('[^0-9a-f]', code):
+    raise ValueError('Registration code %r has invalid characters' % code)
+
+  # Parse payload and CRC as byte strings.
+  payload = binascii.unhexlify(code[0:64])
+  crc = binascii.unhexlify(code[64:72])
+  expected_crc = struct.pack('!I', binascii.crc32(payload) & 0xFFFFFFFF)
+  if expected_crc != crc:
+    raise ValueError('CRC of %r is invalid (should be %s)' %
+                     (code, binascii.hexlify(expected_crc)))
+
diff --git a/py/test/registration_codes_unittest.py b/py/test/registration_codes_unittest.py
new file mode 100755
index 0000000..49e52a1
--- /dev/null
+++ b/py/test/registration_codes_unittest.py
@@ -0,0 +1,36 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+import factory_common  # pylint: disable=W0611
+from cros.factory.test.registration_codes import CheckRegistrationCode
+
+
+class RegistrationCodeTest(unittest.TestCase):
+  def runTest(self):
+    CheckRegistrationCode('000000000000000000000000000000000000'
+                          '0000000000000000000000000000190a55ad')
+    CheckRegistrationCode('010101010101010101010101010101010101'
+                          '010101010101010101010101010162319fcc')
+
+    self.assertRaises(
+        ValueError,
+        lambda: CheckRegistrationCode('00000000'))
+    self.assertRaises(
+        ValueError,
+        lambda: CheckRegistrationCode(
+            '000000000000000000000000000000000000'
+            '0000000000000000000000000000190a55aD'))  # Uppercase D
+    self.assertRaises(
+        ValueError,
+        lambda: CheckRegistrationCode(
+            '000000000000000000000000000000000000'
+            '0000000000000000000000000000190a55ae'))  # Bad CRC
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/py/test/shopfloor.py b/py/test/shopfloor.py
index 5637dbb..975f99f 100644
--- a/py/test/shopfloor.py
+++ b/py/test/shopfloor.py
@@ -1,8 +1,9 @@
-# Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+#!/usr/bin/python
+#
+# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-
 """Wrapper for Factory Shop Floor.
 
 This module provides a simple interface for all factory tests to access ChromeOS
@@ -229,6 +230,11 @@
   """Gets VPD associated with current pinned serial number."""
   return get_instance().GetVPD(get_serial_number())
 
+@_server_api
+def get_registration_code_map():
+  """Gets registration codes associated with current pinned serial number."""
+  return get_instance().GetRegistrationCodeMap(get_serial_number())
+
 
 @_server_api
 def upload_report(blob, name=None):