Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2019-2021 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU General Public License as published by the Free Software
7 : Foundation; either version 3, or (at your option) any later version.
8 :
9 : TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 : A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 :
13 : You should have received a copy of the GNU General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file iban.c
18 : * @brief Common utility function for dealing with IBAN numbers
19 : * @author Florian Dold
20 : */
21 : #include "platform.h"
22 : #include "taler_util.h"
23 :
24 :
25 : /* Country table taken from GNU gettext */
26 :
27 : /**
28 : * Entry in the country table.
29 : */
30 : struct CountryTableEntry
31 : {
32 : /**
33 : * 2-Character international country code.
34 : */
35 : const char *code;
36 :
37 : /**
38 : * Long English name of the country.
39 : */
40 : const char *english;
41 : };
42 :
43 :
44 : /* Keep the following table in sync with gettext.
45 : WARNING: the entries should stay sorted according to the code */
46 : /**
47 : * List of country codes.
48 : */
49 : static const struct CountryTableEntry country_table[] = {
50 : { "AE", "U.A.E." },
51 : { "AF", "Afghanistan" },
52 : { "AL", "Albania" },
53 : { "AM", "Armenia" },
54 : { "AN", "Netherlands Antilles" },
55 : { "AR", "Argentina" },
56 : { "AT", "Austria" },
57 : { "AU", "Australia" },
58 : { "AZ", "Azerbaijan" },
59 : { "BA", "Bosnia and Herzegovina" },
60 : { "BD", "Bangladesh" },
61 : { "BE", "Belgium" },
62 : { "BG", "Bulgaria" },
63 : { "BH", "Bahrain" },
64 : { "BN", "Brunei Darussalam" },
65 : { "BO", "Bolivia" },
66 : { "BR", "Brazil" },
67 : { "BT", "Bhutan" },
68 : { "BY", "Belarus" },
69 : { "BZ", "Belize" },
70 : { "CA", "Canada" },
71 : { "CG", "Congo" },
72 : { "CH", "Switzerland" },
73 : { "CI", "Cote d'Ivoire" },
74 : { "CL", "Chile" },
75 : { "CM", "Cameroon" },
76 : { "CN", "People's Republic of China" },
77 : { "CO", "Colombia" },
78 : { "CR", "Costa Rica" },
79 : { "CS", "Serbia and Montenegro" },
80 : { "CZ", "Czech Republic" },
81 : { "DE", "Germany" },
82 : { "DK", "Denmark" },
83 : { "DO", "Dominican Republic" },
84 : { "DZ", "Algeria" },
85 : { "EC", "Ecuador" },
86 : { "EE", "Estonia" },
87 : { "EG", "Egypt" },
88 : { "ER", "Eritrea" },
89 : { "ES", "Spain" },
90 : { "ET", "Ethiopia" },
91 : { "FI", "Finland" },
92 : { "FO", "Faroe Islands" },
93 : { "FR", "France" },
94 : { "GB", "United Kingdom" },
95 : { "GD", "Caribbean" },
96 : { "GE", "Georgia" },
97 : { "GL", "Greenland" },
98 : { "GR", "Greece" },
99 : { "GT", "Guatemala" },
100 : { "HK", "Hong Kong" },
101 : { "HK", "Hong Kong S.A.R." },
102 : { "HN", "Honduras" },
103 : { "HR", "Croatia" },
104 : { "HT", "Haiti" },
105 : { "HU", "Hungary" },
106 : { "ID", "Indonesia" },
107 : { "IE", "Ireland" },
108 : { "IL", "Israel" },
109 : { "IN", "India" },
110 : { "IQ", "Iraq" },
111 : { "IR", "Iran" },
112 : { "IS", "Iceland" },
113 : { "IT", "Italy" },
114 : { "JM", "Jamaica" },
115 : { "JO", "Jordan" },
116 : { "JP", "Japan" },
117 : { "KE", "Kenya" },
118 : { "KG", "Kyrgyzstan" },
119 : { "KH", "Cambodia" },
120 : { "KR", "South Korea" },
121 : { "KW", "Kuwait" },
122 : { "KZ", "Kazakhstan" },
123 : { "LA", "Laos" },
124 : { "LB", "Lebanon" },
125 : { "LI", "Liechtenstein" },
126 : { "LK", "Sri Lanka" },
127 : { "LT", "Lithuania" },
128 : { "LU", "Luxembourg" },
129 : { "LV", "Latvia" },
130 : { "LY", "Libya" },
131 : { "MA", "Morocco" },
132 : { "MC", "Principality of Monaco" },
133 : { "MD", "Moldava" },
134 : { "MD", "Moldova" },
135 : { "ME", "Montenegro" },
136 : { "MK", "Former Yugoslav Republic of Macedonia" },
137 : { "ML", "Mali" },
138 : { "MM", "Myanmar" },
139 : { "MN", "Mongolia" },
140 : { "MO", "Macau S.A.R." },
141 : { "MT", "Malta" },
142 : { "MV", "Maldives" },
143 : { "MX", "Mexico" },
144 : { "MY", "Malaysia" },
145 : { "NG", "Nigeria" },
146 : { "NI", "Nicaragua" },
147 : { "NL", "Netherlands" },
148 : { "NO", "Norway" },
149 : { "NP", "Nepal" },
150 : { "NZ", "New Zealand" },
151 : { "OM", "Oman" },
152 : { "PA", "Panama" },
153 : { "PE", "Peru" },
154 : { "PH", "Philippines" },
155 : { "PK", "Islamic Republic of Pakistan" },
156 : { "PL", "Poland" },
157 : { "PR", "Puerto Rico" },
158 : { "PT", "Portugal" },
159 : { "PY", "Paraguay" },
160 : { "QA", "Qatar" },
161 : { "RE", "Reunion" },
162 : { "RO", "Romania" },
163 : { "RS", "Serbia" },
164 : { "RU", "Russia" },
165 : { "RW", "Rwanda" },
166 : { "SA", "Saudi Arabia" },
167 : { "SE", "Sweden" },
168 : { "SG", "Singapore" },
169 : { "SI", "Slovenia" },
170 : { "SK", "Slovak" },
171 : { "SN", "Senegal" },
172 : { "SO", "Somalia" },
173 : { "SR", "Suriname" },
174 : { "SV", "El Salvador" },
175 : { "SY", "Syria" },
176 : { "TH", "Thailand" },
177 : { "TJ", "Tajikistan" },
178 : { "TM", "Turkmenistan" },
179 : { "TN", "Tunisia" },
180 : { "TR", "Turkey" },
181 : { "TT", "Trinidad and Tobago" },
182 : { "TW", "Taiwan" },
183 : { "TZ", "Tanzania" },
184 : { "UA", "Ukraine" },
185 : { "US", "United States" },
186 : { "UY", "Uruguay" },
187 : { "VA", "Vatican" },
188 : { "VE", "Venezuela" },
189 : { "VN", "Viet Nam" },
190 : { "YE", "Yemen" },
191 : { "ZA", "South Africa" },
192 : { "ZW", "Zimbabwe" }
193 : };
194 :
195 :
196 : /**
197 : * Country code comparator function, for binary search with bsearch().
198 : *
199 : * @param ptr1 pointer to a `struct table_entry`
200 : * @param ptr2 pointer to a `struct table_entry`
201 : * @return result of memcmp()'ing the 2-digit country codes of the entries
202 : */
203 : static int
204 27 : cmp_country_code (const void *ptr1,
205 : const void *ptr2)
206 : {
207 27 : const struct CountryTableEntry *cc1 = ptr1;
208 27 : const struct CountryTableEntry *cc2 = ptr2;
209 :
210 54 : return memcmp (cc1->code,
211 27 : cc2->code,
212 : 2);
213 : }
214 :
215 :
216 : char *
217 5 : TALER_iban_validate (const char *iban)
218 : {
219 : char cc[2];
220 : char ibancpy[35];
221 : struct CountryTableEntry cc_entry;
222 : unsigned int len;
223 : char *nbuf;
224 : unsigned long long dividend;
225 : unsigned long long remainder;
226 : unsigned int i;
227 : unsigned int j;
228 :
229 5 : if (NULL == iban)
230 0 : return GNUNET_strdup ("(null) is not a valid IBAN");
231 5 : len = strlen (iban);
232 5 : if (len < 4)
233 0 : return GNUNET_strdup ("IBAN number too short to be valid");
234 5 : if (len > 34)
235 0 : return GNUNET_strdup ("IBAN number too long to be valid");
236 5 : memcpy (cc, iban, 2);
237 5 : memcpy (ibancpy, iban + 4, len - 4);
238 5 : memcpy (ibancpy + len - 4, iban, 4);
239 5 : ibancpy[len] = '\0';
240 5 : cc_entry.code = cc;
241 5 : cc_entry.english = NULL;
242 5 : if (NULL ==
243 5 : bsearch (&cc_entry,
244 : country_table,
245 : sizeof (country_table) / sizeof (struct CountryTableEntry),
246 : sizeof (struct CountryTableEntry),
247 : &cmp_country_code))
248 : {
249 : char *msg;
250 :
251 0 : GNUNET_asprintf (&msg,
252 : "Country code `%c%c' not supported\n",
253 0 : cc[0],
254 0 : cc[1]);
255 0 : return msg;
256 : }
257 5 : nbuf = GNUNET_malloc ((len * 2) + 1);
258 120 : for (i = 0, j = 0; i < len; i++)
259 : {
260 115 : if (isalpha ((unsigned char) ibancpy[i]))
261 : {
262 11 : if (2 != snprintf (&nbuf[j],
263 : 3,
264 : "%2u",
265 11 : (ibancpy[i] - 'A' + 10)))
266 : {
267 0 : GNUNET_break (0);
268 0 : return GNUNET_strdup ("internal invariant violation");
269 : }
270 11 : j += 2;
271 11 : continue;
272 : }
273 104 : nbuf[j] = ibancpy[i];
274 104 : j++;
275 : }
276 131 : for (j = 0; '\0' != nbuf[j]; j++)
277 : {
278 126 : if (! isdigit ( (unsigned char) nbuf[j]))
279 : {
280 : char *msg;
281 :
282 0 : GNUNET_asprintf (&msg,
283 : "digit expected at `%s'",
284 : &nbuf[j]);
285 0 : GNUNET_free (nbuf);
286 0 : return msg;
287 : }
288 : }
289 : GNUNET_assert (sizeof(dividend) >= 8);
290 5 : remainder = 0;
291 15 : for (unsigned int i = 0; i<j; i += 16)
292 : {
293 : int nread;
294 :
295 10 : if (1 !=
296 10 : sscanf (&nbuf[i],
297 : "%16llu %n",
298 : ÷nd,
299 : &nread))
300 : {
301 : char *msg;
302 :
303 0 : GNUNET_asprintf (&msg,
304 : "wrong input for checksum calculation at `%s'",
305 : &nbuf[i]);
306 0 : GNUNET_free (nbuf);
307 0 : return msg;
308 : }
309 10 : if (0 != remainder)
310 5 : dividend += remainder * (pow (10, nread));
311 10 : remainder = dividend % 97;
312 : }
313 5 : GNUNET_free (nbuf);
314 5 : if (1 != remainder)
315 0 : return GNUNET_strdup ("IBAN checksum is wrong");
316 5 : return NULL;
317 : }
|