validation_ES_DNI.c (4168B)
1 /* 2 This file is part of Anastasis 3 Copyright (C) 2021 Anastasis SARL 4 5 Anastasis 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 Anastasis 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 Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file reducer/validation_ES_DNI.c 18 * @brief validation logic for Spanish Documento Nacional de Identidad numbers, and Número de Identificación de Extranjeros 19 * @author Christian Grothoff 20 * 21 * Examples: 22 * 12345678Z, 39740191D, 14741806W, X8095495R 23 */ 24 #include <string.h> 25 #include <stdbool.h> 26 #include <stdio.h> 27 28 /** 29 * Function to validate a Spanish CIF number. 30 * 31 * @param civ number to validate 32 * @return true if validation passed, else false 33 */ 34 static bool 35 validate_cif (const char *cif) 36 { 37 size_t slen = strlen (cif); 38 char letter = cif[0]; 39 const char *number = &cif[1]; 40 char control = cif[slen - 1]; 41 unsigned int sum = 0; 42 43 if (9 != slen) 44 return false; 45 46 for (unsigned int i = 0; i < slen - 2; i++) 47 { 48 unsigned int n = number[i] - '0'; 49 50 if (n >= 10) 51 return false; 52 if (0 == (i % 2)) 53 { 54 n *= 2; 55 sum += n < 10 ? n : n - 9; 56 } 57 else 58 { 59 sum += n; 60 } 61 } 62 sum %= 10; 63 if (0 != sum) 64 sum = 10 - sum; 65 { 66 char control_digit = "0123456789"[sum]; 67 char control_letter = "JABCDEFGHI"[sum]; 68 69 switch (letter) 70 { 71 case 'A': 72 case 'B': 73 case 'E': 74 case 'H': 75 return control == control_digit; 76 case 'N': 77 case 'P': 78 case 'Q': 79 case 'R': 80 case 'S': 81 case 'W': 82 return control == control_letter; 83 default: 84 return (control == control_letter) || 85 (control == control_digit); 86 } 87 } 88 } 89 90 91 /** 92 * Function to validate a Spanish DNI number. 93 * 94 * See https://www.ordenacionjuego.es/en/calculo-digito-control 95 * 96 * @param dni_number number to validate (input) 97 * @return true if validation passed, else false 98 */ 99 bool 100 ES_DNI_check (const char *dni_number); 101 102 /* declaration to fix compiler warning */ 103 bool 104 ES_DNI_check (const char *dni_number) 105 { 106 const char map[] = "TRWAGMYFPDXBNJZSQVHLCKE"; 107 unsigned int num; 108 char chksum; 109 unsigned int fact; 110 char dummy; 111 112 if (strlen (dni_number) < 8) 113 return false; 114 switch (dni_number[0]) 115 { 116 case 'A': 117 case 'B': 118 case 'C': 119 case 'D': 120 case 'E': 121 case 'F': 122 case 'G': 123 case 'H': 124 case 'I': 125 case 'J': 126 case 'K': 127 case 'L': 128 case 'N': 129 case 'O': 130 case 'P': 131 case 'Q': 132 case 'R': 133 case 'S': 134 case 'T': 135 case 'U': 136 case 'V': 137 case 'W': 138 /* CIF: [A-W]\d{7}[0-9A-J] */ 139 /* CIF is for companies, we only take those 140 of individuals here! */ 141 return false; /* CIV is not allowed! */ 142 case 'M': 143 /* special NIE, with CIF validation (?), 144 but for individuals, see 145 https://www.strongabogados.com/tax-id-spain.php */ 146 return validate_cif (dni_number); 147 case 'X': 148 case 'Y': 149 case 'Z': 150 /* NIE */ 151 fact = dni_number[0] - 'X'; 152 /* 7 or 8 digits */ 153 if (2 == sscanf (&dni_number[1], 154 "%8u%c%c", 155 &num, 156 &chksum, 157 &dummy)) 158 { 159 num += fact * 100000000; 160 } 161 else if (2 == sscanf (&dni_number[1], 162 "%7u%c%c", 163 &num, 164 &chksum, 165 &dummy)) 166 { 167 num += fact * 10000000; 168 } 169 else 170 { 171 return false; 172 } 173 break; 174 default: 175 fact = 0; 176 /* DNI */ 177 if (2 != sscanf (dni_number, 178 "%8u%c%c", 179 &num, 180 &chksum, 181 &dummy)) 182 return false; 183 break; 184 } 185 if (map[num % 23] != chksum) 186 return false; 187 return true; 188 }