1 /*
2 * Copyright (c) 2024, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /**
30 * @file
31 * This file implements Verhoeff checksum calculation and validation.
32 */
33
34 #include "verhoeff_checksum.hpp"
35
36 #if OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE
37
38 #include "common/code_utils.hpp"
39 #include "common/string.hpp"
40
41 namespace ot {
42 namespace Utils {
43
Lookup(uint8_t aIndex,const uint8_t aCompressedArray[])44 uint8_t VerhoeffChecksum::Lookup(uint8_t aIndex, const uint8_t aCompressedArray[])
45 {
46 // The values in the array are [0-9]. To save space, two
47 // entries are saved as a single byte in @p aCompressedArray,
48 // such that higher 4-bit corresponds to one entry, and the
49 // lower 4-bit to the next entry.
50
51 uint8_t result = aCompressedArray[aIndex / 2];
52
53 if ((aIndex & 1) == 0)
54 {
55 result >>= 4;
56 }
57 else
58 {
59 result &= 0x0f;
60 }
61
62 return result;
63 }
64
Multiply(uint8_t aFirst,uint8_t aSecond)65 uint8_t VerhoeffChecksum::Multiply(uint8_t aFirst, uint8_t aSecond)
66 {
67 static uint8_t kMultiplication[][5] = {{0x01, 0x23, 0x45, 0x67, 0x89}, {0x12, 0x34, 0x06, 0x78, 0x95},
68 {0x23, 0x40, 0x17, 0x89, 0x56}, {0x34, 0x01, 0x28, 0x95, 0x67},
69 {0x40, 0x12, 0x39, 0x56, 0x78}, {0x59, 0x87, 0x60, 0x43, 0x21},
70 {0x65, 0x98, 0x71, 0x04, 0x32}, {0x76, 0x59, 0x82, 0x10, 0x43},
71 {0x87, 0x65, 0x93, 0x21, 0x04}, {0x98, 0x76, 0x54, 0x32, 0x10}};
72
73 return Lookup(aSecond, kMultiplication[aFirst]);
74 }
75
Permute(uint8_t aPosition,uint8_t aValue)76 uint8_t VerhoeffChecksum::Permute(uint8_t aPosition, uint8_t aValue)
77 {
78 static uint8_t kPermutation[][5] = {{0x01, 0x23, 0x45, 0x67, 0x89}, {0x15, 0x76, 0x28, 0x30, 0x94},
79 {0x58, 0x03, 0x79, 0x61, 0x42}, {0x89, 0x16, 0x04, 0x35, 0x27},
80 {0x94, 0x53, 0x12, 0x68, 0x70}, {0x42, 0x86, 0x57, 0x39, 0x01},
81 {0x27, 0x93, 0x80, 0x64, 0x15}, {0x70, 0x46, 0x91, 0x32, 0x58}};
82
83 return Lookup(aValue, kPermutation[aPosition]);
84 }
85
InverseOf(uint8_t aValue)86 uint8_t VerhoeffChecksum::InverseOf(uint8_t aValue)
87 {
88 static uint8_t kInverse[] = {0x04, 0x32, 0x15, 0x67, 0x89};
89
90 return Lookup(aValue, kInverse);
91 }
92
Calculate(const char * aDecimalString,char & aChecksum)93 Error VerhoeffChecksum::Calculate(const char *aDecimalString, char &aChecksum)
94 {
95 Error error;
96 uint8_t code;
97
98 SuccessOrExit(error = ComputeCode(aDecimalString, code, /* aValidate */ false));
99 aChecksum = static_cast<char>('0' + InverseOf(code));
100
101 exit:
102 return error;
103 }
104
Validate(const char * aDecimalString)105 Error VerhoeffChecksum::Validate(const char *aDecimalString)
106 {
107 Error error;
108 uint8_t code;
109
110 SuccessOrExit(error = ComputeCode(aDecimalString, code, /* aValidate */ true));
111 VerifyOrExit(code == 0, error = kErrorFailed);
112
113 exit:
114 return error;
115 }
116
ComputeCode(const char * aDecimalString,uint8_t & aCode,bool aValidate)117 Error VerhoeffChecksum::ComputeCode(const char *aDecimalString, uint8_t &aCode, bool aValidate)
118 {
119 Error error = kErrorNone;
120 uint8_t code = 0;
121 uint16_t index = 0;
122 uint16_t length = StringLength(aDecimalString, kMaxStringLength + 1);
123
124 VerifyOrExit(length <= kMaxStringLength, error = kErrorInvalidArgs);
125
126 if (!aValidate)
127 {
128 length++;
129 index++;
130 }
131
132 for (; index < length; ++index)
133 {
134 char digit = aDecimalString[length - index - 1];
135
136 VerifyOrExit(digit >= '0' && digit <= '9', error = kErrorInvalidArgs);
137 code = Multiply(code, Permute(index % 8, static_cast<uint8_t>(digit - '0')));
138 }
139
140 aCode = code;
141
142 exit:
143 return error;
144 }
145
146 } // namespace Utils
147 } // namespace ot
148
149 #endif // OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE
150