anastasis

Credential backup and recovery protocol and service
Log | Files | Refs | Submodules | README | LICENSE

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 }