The code128 checksum is not cryptographically secure.
I think you are misunderstanding the purpose of a checksum. A checksum is designed to make it possible to detect accidental corruption or mistakes in reading data. The data is converted into a checksum, and compared with the expected checksum. If the two differ, you know that there was an error in reading the data. The intention is to reduce the chance that an accidental mistake in reading results in the same checksum. The checksum algorithm itself is public and is easy to calculate, such as with the following function, where code128_table
is a specific index table (Code A, B, or C):
uint8_t code128_checksum(uint8_t *buf, uint32_t len)
{
uint32_t sum = *buf - 32;
while (--len)
sum += len * code128_table[*(buf + len)];
return sum % 103 + 32;
}
This checksum is extremely simple, designed only to make detection of errors during the scanning process possible. It is a "true" sum, meaning that it is nothing more than the summation of the representations of each component character multiplied by a weight factor. Thus the sum is literally just that, a sum of the data it is protecting. It is not designed to provide any sort of cryptographic properties to the barcode. The way it is designed, all the following are trivially possible, in order of increasing computational complexity:
- Hashing - Given an un-checksummed barcode, calculate the checksum.
- Collision - Create two barcodes with different contents but an identical checksum.
- Second preimage - Given a checksummed barcode, create another with the same checksum.
- First preimage - Given only a checksum, create a barcode with the same checksum.
The only thing which it is (modestly) good at is ensuring that a random change to the barcode will not result in an identical checksum. Even for that, it's not particularly good. A superior (albeit still not cryptographically secure) checksum of the same digest size would be CRC8, as a CRC is superior to a regular checksum for the purpose of error detection. A simple implementation of an 8-bit CRC (where crc8_table
is a carefully selected 8-bit CRC polynomial) would look like this:
uint8_t crc8(uint8_t *buf, uint32_t len)
{
uint8_t crc = ~0;
while (len--)
crc = crc8_table[crc ^ *buf++];
return crc;
}
Regardless of whether you use a simple checksum, CRC, or a significantly more complex and slower cryptographically secure hash function, if the keyspace is merely 8 decimal characters and all 8 characters from the sequential ID are valid for the company ID B2DS, you could, quite trivially, create all 99,999,998 possible barcodes, with matching associated checksums. A completely unoptimized implementation took 19 seconds on my own computer. The keyspace is just too small, even with a cryptographically secure hash.
As @Philipp mentioned in a comment, a better solution would be to hand out barcodes with a random ID. Allow each ID to be used a limited number of times, and monitor for attempts to use a barcode with an ID which has not been explicitly issued. If that is done, then even if there are a thousand valid barcodes at any given time, a random barcode would have only a one in ten thousand chance of being valid, making it far harder to use the codes fraudulently.