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