1 /*
2 * Simple library to detect and validate SSN and Credit Card numbers.
3 *
4 * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
5 * Copyright (C) 2007-2013 Sourcefire, Inc.
6 *
7 * Authors: Martin Roesch <roesch@sourcefire.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21 * MA 02110-1301, USA.
22 */
23
24 #if HAVE_CONFIG_H
25 #include "clamav-config.h"
26 #endif
27
28 #include <stdio.h>
29 #include <ctype.h>
30 #include <string.h>
31 #include <stdarg.h>
32 #include <stdlib.h>
33 #include "dlp.h"
34 #include "others.h"
35 #include "str.h"
36
37 /* detection mode macros for the contains_* functions */
38 #define DETECT_MODE_DETECT 0
39 #define DETECT_MODE_COUNT 1
40
41 #define IIN_SIZE 6
42 #define MAX_CC_BREAKS 8
43
44 /* group number mapping is here */
45 /* http://www.socialsecurity.gov/employer/highgroup.txt */
46 /* here's a perl script to convert the raw data from the highgroup.txt
47 * file to the data set in ssn_max_group[]:
48 --
49 local $/;
50 my $i = <>;
51 my $count = 0;
52 while ($i =~ s/(\d{3}) (\d{2})//) {
53 print int($2) .", ";
54 if ($count == 18)
55 {
56 print "\n";
57 $count = 0;
58 }
59 else
60 {
61 $count++;
62 }
63 }
64 --
65 *
66 * run 'perl convert.pl < highgroup.txt' to generate the data
67 *
68 */
69
70 /* MAX_AREA is the maximum assigned area number. This can be derived from
71 * the data in the highgroup.txt file by looking at the last area->group
72 * mapping from that file.
73 */
74 #define MAX_AREA 772
75
76 /* array of max group numbers for a given area number */
77 /*
78 static int ssn_max_group[MAX_AREA+1] = { 0,
79 6, 6, 4, 8, 8, 8, 6, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90,
80 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 88, 88, 88, 88, 72, 72, 72, 72,
81 70, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 96, 96, 96, 96, 96, 96, 96, 96,
82 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,
83 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,
84 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,
85 96, 96, 96, 96, 96, 96, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94,
86 94, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 17, 17, 17, 17, 17, 17,
87 17, 17, 17, 17, 17, 17, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84,
88 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 82, 82, 82, 82, 82, 82, 82, 82,
89 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82,
90 82, 82, 79, 79, 79, 79, 79, 79, 79, 79, 77, 6, 4, 99, 99, 99, 99, 99, 99,
91 99, 99, 99, 53, 53, 53, 53, 53, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
92 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
93 99, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
94 13, 13, 13, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 33, 33,
95 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 6, 6, 6, 6, 6, 6,
96 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
97 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, 4, 4, 4, 4, 4, 4, 4, 4,
98 35, 35, 35, 35, 35, 35, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
99 33, 33, 33, 33, 33, 33, 29, 29, 29, 29, 29, 29, 29, 29, 27, 27, 27, 27, 27,
100 67, 67, 67, 67, 67, 67, 67, 67, 99, 99, 99, 99, 99, 99, 99, 99, 63, 61, 61,
101 61, 61, 61, 61, 61, 61, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
102 99, 99, 23, 23, 23, 23, 23, 23, 23, 21, 21, 99, 99, 99, 99, 99, 99, 99, 99,
103 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 51, 51, 51, 51, 49, 49, 49, 49,
104 49, 49, 37, 37, 37, 37, 37, 37, 37, 37, 25, 25, 25, 25, 25, 25, 25, 25, 25,
105 25, 25, 25, 23, 23, 23, 33, 33, 41, 39, 53, 51, 51, 51, 27, 27, 27, 27, 27,
106 27, 27, 45, 43, 79, 77, 55, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 63, 63,
107 63, 61, 61, 61, 61, 61, 61, 75, 73, 73, 73, 73, 99, 99, 99, 99, 99, 99, 99,
108 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
109 99, 99, 99, 51, 99, 99, 45, 45, 43, 37, 99, 99, 99, 99, 99, 61, 99, 3, 99,
110 99, 99, 99, 99, 99, 99, 84, 84, 84, 84, 99, 99, 67, 67, 65, 65, 65, 65, 65,
111 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 11,
112 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 96,
113 96, 44, 44, 46, 46, 46, 44, 28, 26, 26, 26, 26, 16, 16, 16, 14, 14, 14, 14,
114 36, 34, 34, 34, 34, 34, 34, 34, 34, 14, 14, 12, 12, 90, 14, 14, 14, 14, 12,
115 12, 12, 12, 12, 12, 9, 9, 7, 7, 7, 7, 7, 7, 7, 18, 18, 18, 18, 18,
116 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
117 28, 18, 18, 10, 14, 10, 10, 10, 10, 10, 9, 9, 3, 1, 5, 5, 5, 5, 5,
118 5, 3, 3, 82, 82, 66, 66, 64, 64, 64, 64, 64
119 };
120 */
121
122 /*
123 Following is a table of payment card "issuer identification number" ranges
124 and additional info such as card number length.
125 */
126
127 struct iin_map_struct {
128 uint32_t iin_start;
129 uint32_t iin_end;
130 uint8_t card_min;
131 uint8_t card_max;
132 uint8_t is_cc;
133 uint8_t luhn;
134 const char *iin_name;
135 };
136
137 static struct iin_map_struct iin_map[] = {
138 {100000, 199999, 13, 15, 0, 1, "UATP"},
139 {222100, 272099, 16, 16, 1, 1, "Mastercard 2016"},
140 {300000, 305999, 14, 16, 1, 1, "Diner's Club - Carte Blanche"},
141 {309500, 309599, 14, 16, 1, 1, "Diner's Club International"},
142 {340000, 349999, 15, 15, 1, 1, "American Express"},
143 {352800, 358999, 16, 16, 1, 1, "JCB"},
144 {360000, 369999, 14, 16, 1, 1, "Diner's Club International"},
145 {370000, 379999, 15, 15, 1, 1, "American Express"},
146 {380000, 399999, 16, 16, 1, 1, "Diner's Club International"},
147 {400000, 499999, 16, 16, 1, 1, "Visa"},
148 {500000, 509999, 16, 16, 0, 1, "Maestro"},
149 {510000, 559999, 16, 16, 1, 1, "Master Card"},
150 {601100, 601199, 16, 16, 1, 1, "Discover"},
151 {622126, 622926, 16, 16, 1, 1, "China Union Pay"},
152 {624000, 626999, 16, 16, 1, 1, "China Union Pay"},
153 {628200, 628899, 16, 16, 1, 1, "China Union Pay"},
154 {644000, 659999, 16, 16, 1, 1, "Discover 2009"},
155 {0}};
156
get_iin(char * digits,int cc_only)157 static const struct iin_map_struct *get_iin(char *digits, int cc_only)
158 {
159 uint32_t iin = atoi(digits);
160 int i = 0;
161
162 while (iin_map[i].iin_start != 0) {
163 if (iin < iin_map[i].iin_start)
164 break;
165 if (iin <= iin_map[i].iin_end && (cc_only == 0 || iin_map[i].is_cc == 1)) {
166 cli_dbgmsg("Credit card IIN %s matched range for %s\n", digits, iin_map[i].iin_name);
167 return &iin_map[i];
168 }
169 i++;
170 }
171 cli_dbgmsg("Credit card %s did not match an IIN range\n", digits);
172 return NULL;
173 }
174
dlp_is_valid_cc(const unsigned char * buffer,size_t length,int cc_only)175 int dlp_is_valid_cc(const unsigned char *buffer, size_t length, int cc_only)
176 {
177 int mult = 0;
178 int sum = 0;
179 size_t i = 0;
180 ssize_t j = 0;
181 int val = 0;
182 size_t digits = 0;
183 char cc_digits[20];
184 size_t pad_allowance = MAX_CC_BREAKS;
185 const struct iin_map_struct *iin;
186
187 if (buffer == NULL || length < 13)
188 return 0;
189 /* if the first digit is greater than 6 it isn't one of the major
190 * credit cards
191 * reference => http://www.beachnet.com/~hstiles/cardtype.html
192 */
193 if (!isdigit(buffer[0]) || buffer[0] > '6' || buffer[0] == 2)
194 return 0;
195
196 if (length > 19 + pad_allowance) /* max credit card length is 19, with allowance for punctuation */
197 length = 19 + pad_allowance;
198
199 /* Look for possible 6 digit IIN */
200 for (i = 0; i < length && digits < IIN_SIZE; i++) {
201 if (isdigit(buffer[i]) == 0) {
202 if (buffer[i] == ' ' || buffer[i] == '-')
203 if (pad_allowance-- > 0)
204 continue;
205 break;
206 }
207 cc_digits[digits] = buffer[i];
208 digits++;
209 }
210
211 if (digits == IIN_SIZE)
212 cc_digits[digits] = 0;
213 else
214 return 0;
215
216 /* See if it is a valid IIN. */
217 iin = get_iin(cc_digits, cc_only);
218 if (iin == NULL)
219 return 0;
220
221 /* Look for the remaining needed digits. */
222 for (/*same 'i' from previous for-loop*/; i < length && digits < iin->card_max; i++) {
223 if (isdigit(buffer[i]) == 0) {
224 if (buffer[i] == ' ' || buffer[i] == '-')
225 if (pad_allowance-- > 0)
226 continue;
227 break;
228 }
229 cc_digits[digits] = buffer[i];
230 digits++;
231 }
232
233 if (digits < iin->card_min || (i < length && isdigit(buffer[i])))
234 return 0;
235
236 j = (ssize_t)i;
237 //figure out luhn digits
238 for (j = digits - 1; j >= 0; j--) {
239 val = cc_digits[j] - '0';
240 if (mult) {
241 if ((val *= 2) > 9) val -= 9;
242 }
243 mult = !mult;
244 sum += val;
245 }
246
247 if (sum % 10)
248 return 0;
249
250 cli_dbgmsg("Luhn algorithm successful for %s\n", cc_digits);
251
252 return 1;
253 }
254
contains_cc(const unsigned char * buffer,size_t length,int detmode,int cc_only)255 static int contains_cc(const unsigned char *buffer, size_t length, int detmode, int cc_only)
256 {
257 const unsigned char *idx;
258 const unsigned char *end;
259 int count = 0;
260
261 if (buffer == NULL || length < 13) {
262 return 0;
263 }
264
265 end = buffer + length;
266 idx = buffer;
267
268 while (idx < end) {
269 if (isdigit(*idx)) {
270 if ((idx == buffer || !isdigit(idx[-1])) && dlp_is_valid_cc(idx, length - (idx - buffer), cc_only) == 1) {
271 if (detmode == DETECT_MODE_DETECT)
272 return 1;
273 else {
274 count++;
275 /* if we got a valid match we should increment the idx ptr
276 * to gain a little performance
277 */
278 idx += (length > 15 ? 15 : (length - 1));
279 }
280 }
281 }
282 idx++;
283 }
284
285 return count;
286 }
287
dlp_get_cc_count(const unsigned char * buffer,size_t length,int cc_only)288 int dlp_get_cc_count(const unsigned char *buffer, size_t length, int cc_only)
289 {
290 return contains_cc(buffer, length, DETECT_MODE_COUNT, cc_only);
291 }
292
dlp_has_cc(const unsigned char * buffer,size_t length,int cc_only)293 int dlp_has_cc(const unsigned char *buffer, size_t length, int cc_only)
294 {
295 return contains_cc(buffer, length, DETECT_MODE_DETECT, cc_only);
296 }
297
dlp_is_valid_ssn(const unsigned char * buffer,size_t length,int format)298 int dlp_is_valid_ssn(const unsigned char *buffer, size_t length, int format)
299 {
300 int area_number;
301 int group_number;
302 int serial_number;
303 size_t minlength;
304 int retval = 1;
305 char numbuf[12];
306
307 if (buffer == NULL)
308 return 0;
309
310 minlength = (format == SSN_FORMAT_HYPHENS ? 11 : 9);
311
312 if (length < minlength)
313 return 0;
314
315 if ((length > minlength) && isdigit(buffer[minlength]))
316 return 0;
317
318 strncpy(numbuf, (const char *)buffer, minlength);
319 numbuf[minlength] = 0;
320
321 /* sscanf parses and (basically) validates the string for us */
322 switch (format) {
323 case SSN_FORMAT_HYPHENS:
324 if (numbuf[3] != '-' || numbuf[6] != '-')
325 return 0;
326
327 if (sscanf((const char *)numbuf,
328 "%3d-%2d-%4d",
329 &area_number,
330 &group_number,
331 &serial_number) != 3) {
332 return 0;
333 }
334 break;
335 case SSN_FORMAT_STRIPPED:
336 if (!cli_isnumber(numbuf))
337 return 0;
338
339 if (sscanf((const char *)numbuf,
340 "%3d%2d%4d",
341 &area_number,
342 &group_number,
343 &serial_number) != 3) {
344 return 0;
345 }
346 break;
347 default:
348 cli_dbgmsg("dlp_is_valid_ssn: unknown format type %d \n", format);
349 return 0;
350 }
351
352 /* start validating */
353 /* validation data taken from
354 * http://en.wikipedia.org/wiki/Social_Security_number_%28United_States%29
355 */
356 if (area_number > MAX_AREA ||
357 area_number == 666 ||
358 area_number <= 0 ||
359 group_number <= 0 ||
360 group_number > 99 ||
361 serial_number <= 0 ||
362 serial_number > 9999)
363 retval = 0;
364
365 if (area_number == 987 && group_number == 65) {
366 if (serial_number >= 4320 && serial_number <= 4329)
367 retval = 0;
368 }
369
370 /*
371 if(group_number > ssn_max_group[area_number])
372 retval = 0;
373 */
374 if (retval)
375 cli_dbgmsg("dlp_is_valid_ssn: SSN_%s: %s\n", format == SSN_FORMAT_HYPHENS ? "HYPHENS" : "STRIPPED", numbuf);
376
377 return retval;
378 }
379
contains_ssn(const unsigned char * buffer,size_t length,int format,int detmode)380 static int contains_ssn(const unsigned char *buffer, size_t length, int format, int detmode)
381 {
382 const unsigned char *idx;
383 const unsigned char *end;
384 int count = 0;
385
386 if (buffer == NULL || length < 9)
387 return 0;
388
389 end = buffer + length;
390 idx = buffer;
391 while (idx < end) {
392 if (isdigit(*idx)) {
393 /* check for area number and the first hyphen */
394 if ((idx == buffer || !isdigit(idx[-1])) && dlp_is_valid_ssn(idx, length - (idx - buffer), format) == 1) {
395 if (detmode == DETECT_MODE_COUNT) {
396 count++;
397 /* hop over the matched bytes if we found an SSN */
398 idx += ((format == SSN_FORMAT_HYPHENS) ? 11 : 9);
399 } else {
400 return 1;
401 }
402 }
403 }
404 idx++;
405 }
406
407 return count;
408 }
409
dlp_get_stripped_ssn_count(const unsigned char * buffer,size_t length)410 int dlp_get_stripped_ssn_count(const unsigned char *buffer, size_t length)
411 {
412 return contains_ssn(buffer,
413 length,
414 SSN_FORMAT_STRIPPED,
415 DETECT_MODE_COUNT);
416 }
417
dlp_get_normal_ssn_count(const unsigned char * buffer,size_t length)418 int dlp_get_normal_ssn_count(const unsigned char *buffer, size_t length)
419 {
420 return contains_ssn(buffer,
421 length,
422 SSN_FORMAT_HYPHENS,
423 DETECT_MODE_COUNT);
424 }
425
dlp_get_ssn_count(const unsigned char * buffer,size_t length)426 int dlp_get_ssn_count(const unsigned char *buffer, size_t length)
427 {
428 /* this will suck for performance but will find SSNs in either
429 * format
430 */
431 return (dlp_get_stripped_ssn_count(buffer, length) + dlp_get_normal_ssn_count(buffer, length));
432 }
433
dlp_has_ssn(const unsigned char * buffer,size_t length)434 int dlp_has_ssn(const unsigned char *buffer, size_t length)
435 {
436 return (contains_ssn(buffer,
437 length,
438 SSN_FORMAT_HYPHENS,
439 DETECT_MODE_DETECT) |
440 contains_ssn(buffer,
441 length,
442 SSN_FORMAT_STRIPPED,
443 DETECT_MODE_DETECT));
444 }
445
dlp_has_stripped_ssn(const unsigned char * buffer,size_t length)446 int dlp_has_stripped_ssn(const unsigned char *buffer, size_t length)
447 {
448 return contains_ssn(buffer,
449 length,
450 SSN_FORMAT_STRIPPED,
451 DETECT_MODE_DETECT);
452 }
453
dlp_has_normal_ssn(const unsigned char * buffer,size_t length)454 int dlp_has_normal_ssn(const unsigned char *buffer, size_t length)
455 {
456 return contains_ssn(buffer,
457 length,
458 SSN_FORMAT_HYPHENS,
459 DETECT_MODE_DETECT);
460 }
461
462 /* The program below checks for the instances of where a */
463 /* Canadian Bank Routing Number or EFT is found, or if a */
464 /* U.S. MICR Bank Routing Number is encountered. */
465
466 /* Author: Bill Parker */
467 /* Date: February 17, 2013 */
468 /* Last Modified: February 25, 2013 */
469
470 /* Purpose: To provide Snort and ClamAV the ability to */
471 /* detect canadian and U.S. bank routing transaction */
472 /* numbers via the DLP module in ClamAV or the SDF pre- */
473 /* processor in the Snort IDS. */
474
475 /* Are first three or last three digits a valid bank code */
is_bank_code_valid(int bank_code)476 int is_bank_code_valid(int bank_code)
477 {
478 switch (bank_code) {
479 case 1:
480 return 1; /* Bank of Montreal */
481 case 2:
482 return 1; /* Bank of Nova Scotia */
483 case 3:
484 return 1; /* Royal Bank of Canada */
485 case 4:
486 return 1; /* Toronto-Dominion Bank */
487 case 6:
488 return 1; /* National Bank of Canada */
489 case 10:
490 return 1; /* Canadian Imperial Bank of Commerce */
491 case 16:
492 return 1; /* HSBC Canada */
493 case 30:
494 return 1; /* Canadian Western Bank */
495 case 39:
496 return 1; /* Laurentian Bank of Canada */
497 case 117:
498 return 1; /* Government of Canada */
499 case 127:
500 return 1; /* Canada Post (Money Orders) */
501 case 177:
502 return 1; /* Bank of Canada */
503 case 219:
504 return 1; /* ATB Financial */
505 case 260:
506 return 1; /* Citibank Canada */
507 case 290:
508 return 1; /* UBS Bank (Canada) */
509 case 308:
510 return 1; /* Bank of China (Canada) */
511 case 309:
512 return 1; /* Citizens Bank of Canada */
513 case 326:
514 return 1; /* President’s Choice Financial */
515 case 338:
516 return 1; /* Canadian Tire Bank */
517 case 340:
518 return 1; /* ICICI Bank Canada */
519 case 509:
520 return 1; /* Canada Trust */
521 case 540:
522 return 1; /* Manulife Bank */
523 case 614:
524 return 1; /* ING Direct Canada */
525 case 809:
526 return 1; /* Central 1 [Credit Union] – BC Region */
527 case 815:
528 return 1; /* Caisses Desjardins du Québec */
529 case 819:
530 return 1; /* Caisses populaires Desjardins du Manitoba */
531 case 828:
532 return 1; /* Central 1 [Credit Union] – ON Region */
533 case 829:
534 return 1; /* Caisses populaires Desjardins de l’Ontario */
535 case 837:
536 return 1; /* Meridian Credit Union */
537 case 839:
538 return 1; /* Credit Union Heritage (Nova Scotia) */
539 case 865:
540 return 1; /* Caisses populaires Desjardins acadiennes */
541 case 879:
542 return 1; /* Credit Union Central of Manitoba */
543 case 889:
544 return 1; /* Credit Union Central of Saskatchewan */
545 case 899:
546 return 1; /* Credit Union Central Alberta */
547 case 900:
548 return 1; /* Unknown??? */
549 default:
550 return 0; /* NO MATCH...FAIL */
551 } /* end if switch(bank_code) */
552
553 return 0;
554 } /* end function is_bank_code_valid() */
555
556 /* This function checks if the supplied string is a valid */
557 /* canadian transit number, the format is as follows: */
558
559 /* XXXXX-YYY where XXXXX is a branch number, and YYY is */
560 /* the institutional number. */
561
562 /* note: it does NOT appear that the canadian RTN or EFT */
563 /* number formats contain any type of checksum algorithm */
564 /* or a check digit. */
cdn_ctn_is_valid(const char * buffer,size_t length)565 int cdn_ctn_is_valid(const char *buffer, size_t length)
566 {
567 int i;
568 int bank_code = 0; /* last three digits of Canada RTN/MICR is Bank I.D. */
569
570 if (buffer == NULL || length < 9) /* if the buffer is empty or */
571 return 0; /* the length is less than 9, it's not valid */
572
573 if (buffer[5] != '-') return 0; /* if the 6th char isn't a '-', not a valid RTN */
574
575 for (i = 0; i < 5; i++)
576 if (isdigit(buffer[i]) == 0)
577 return 0;
578
579 /* Check the various branch codes which are listed, but there */
580 /* may be more valid codes which could be added as well... */
581
582 /* convert last three elements in buffer to a numeric value */
583
584 for (i = 6; i < 9; i++) {
585 if (isdigit(buffer[i]) == 0)
586 return 0;
587 bank_code = (bank_code * 10) + (buffer[i] - '0');
588 }
589
590 /* now have a switch sandwich for bank codes */
591 return (is_bank_code_valid(bank_code)); /* return 1 if valid, 0 if not */
592 }
593
594 /* If the string is a canadian EFT (Electronic Fund */
595 /* Transaction), the format is as follows: */
596
597 /* 0YYYXXXX, where a leading zero is required, XXXXX is a */
598 /* branch number, and YYY is the institution number. */
599
600 /* note: it does NOT appear that the canadian RTN or EFT */
601 /* number formats contain any type of checksum algorithm */
602 /* or a check digit. */
603
cdn_eft_is_valid(const char * buffer,size_t length)604 int cdn_eft_is_valid(const char *buffer, size_t length)
605 {
606 int bank_code = 0;
607 int i;
608
609 if (buffer == NULL || length < 9) /* if the buffer is empty or */
610 return 0; /* the length is less than 9, it's not valid */
611
612 if (buffer[0] != '0') return 0; /* if the 1st char isn't a '0', not a valid EFT */
613
614 for (i = 1; i < 4; i++) {
615 if (isdigit(buffer[i]) == 0)
616 return 0;
617 bank_code = (bank_code * 10) + (buffer[i] - '0');
618 }
619
620 /* Check the various branch codes which are listed, but there */
621 /* may be more valid codes which could be added as well... */
622 if (!is_bank_code_valid(bank_code))
623 return 0;
624
625 for (i = 4; i < 9; i++)
626 if (isdigit(buffer[i]) == 0)
627 return 0;
628
629 return 1;
630 }
631
us_micr_is_valid(const char * buffer,size_t length)632 int us_micr_is_valid(const char *buffer, size_t length)
633 {
634 int result, sum, sum1, sum2, sum3;
635 int i;
636 unsigned char micr_digits[9];
637
638 if (buffer == NULL || length < 9) /* if the buffer is empty or */
639 return 0; /* the length is < 9, it's not valid */
640
641 /* loop and make sure all the characters are actually digits */
642
643 for (i = 0; i < 9; i++) {
644 if (isdigit(buffer[i]) == 0)
645 return 0;
646 micr_digits[i] = buffer[i];
647 }
648
649 /* see if we have a valid MICR number via the following formula */
650
651 /* 7 * (micr_digits[0] + micr_digits[3] + micr_digits[6]) + */
652 /* 3 * (micr_digits[1] + micr_digits[4] + micr_digits[7]) + */
653 /* 9 * (micr_digits[2] + micr_digits[5]) (the check digit is */
654 /* computed by the sum above modulus 10 */
655
656 sum1 = 7 * ((micr_digits[0] - '0') + (micr_digits[3] - '0') + (micr_digits[6] - '0'));
657 sum2 = 3 * ((micr_digits[1] - '0') + (micr_digits[4] - '0') + (micr_digits[7] - '0'));
658 sum3 = 9 * ((micr_digits[2] - '0') + (micr_digits[5] - '0'));
659 sum = sum1 + sum2 + sum3;
660 result = sum % 10;
661
662 if (result == (micr_digits[8] - '0'))
663 return 1; /* last digit of MICR matches result */
664 return 0; /* MICR number isn't valid */
665 }
666