blob: 785b396e5a17fffa2d92144f6976cd30aeb95bde [file] [log] [blame]
#!/usr/bin/env python2
# Copyright 2013 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.
"""Implementation of base32 utilities."""
from __future__ import print_function
import argparse
from six.moves import xrange
from zlib import crc32
import factory_common # pylint: disable=unused-import
from cros.factory.hwid.v3 import common
class Base32(object):
"""A utility class for encoding binary string to base32 string, decoding
base32 string to binary string, and calculating 10-bit base32 checksum.
The base32 encoding used here is not identical to the standard as described
in: http://tools.ietf.org/html/rfc4648. We encode arbitrary length of bit
strings and pad 0 when the bit string length is not multiples of 5.
"""
BASE32_ALPHABET = common.HEADER_ALPHABET
BASE32_REVERSED = dict([v, k] for k, v in enumerate(BASE32_ALPHABET))
BASE32_BIT_WIDTH = 5
DASH_INSERTION_WIDTH = 4
CHECKSUM_SIZE = 10
ENCODED_CHECKSUM_SIZE = 2
@classmethod
def GetPaddingLength(cls, orig_length):
"""Returns the minimum padding length for a given length.
Args:
orig_length: The length to be calculated.
Returns:
A number.
"""
return (cls.BASE32_BIT_WIDTH - orig_length) % cls.BASE32_BIT_WIDTH
@classmethod
def Encode(cls, binary_string):
"""Converts the given binary string to a base32-encoded string. Add paddings
if necessary.
Args:
binary_string: A binary string.
Returns:
A base32-encoded string.
"""
assert cls.GetPaddingLength(len(binary_string)) == 0
result = []
for i in xrange(0, len(binary_string), cls.BASE32_BIT_WIDTH):
result.append(cls.BASE32_ALPHABET[
int(binary_string[i:i + cls.BASE32_BIT_WIDTH], 2)])
return ''.join(result)
@classmethod
def Decode(cls, base32_string):
"""Converts the given base32-encoded string to a binary string.
Args:
base32_string: A base32-encoded string.
Returns:
A binary string.
"""
result = []
for c in base32_string:
result.append('{0:05b}'.format(cls.BASE32_REVERSED[c.upper()]))
return ''.join(result)
@classmethod
def Checksum(cls, string):
"""Calculate a 10-bit checksum for the given string.
Args:
string: A string to generate checksum for.
Returns:
A string with two base32-encoded alphabets representing the
10-bit checksum.
"""
# Get the last 10 bits
c = crc32(string) & (2 ** 10 - 1)
return (cls.BASE32_ALPHABET[c >> cls.BASE32_BIT_WIDTH] +
cls.BASE32_ALPHABET[c & (2 ** cls.BASE32_BIT_WIDTH - 1)])
if __name__ == '__main__':
option_parser = argparse.ArgumentParser(
description='Command-line interface for base32 encoding.')
option_parser.add_argument('hwid', metavar='HWID', help='HWID to operate on.')
option_parser.add_argument('--checksum', '-c', action='store_true',
help='Calculate checksum of the given HWID.')
option_parser.add_argument('--verify-checksum', '-v', action='store_true',
help='Verify checksum of the given HWID.')
options = option_parser.parse_args()
stripped_hwid = options.hwid.upper().replace('-', '')
if options.checksum:
print(Base32.Checksum(stripped_hwid))
elif options.verify_checksum:
expected_checksum = Base32.Checksum(stripped_hwid[:-2])
given_checksum = stripped_hwid[-2:]
if expected_checksum == given_checksum:
print('Success.')
else:
print('Checksum should be: %r' % expected_checksum)
else:
option_parser.print_help()