#!/usr/bin/perl ## CH ID checksums ## IDCHE123412345 7 ## 0109110M1001015CHE<2 ## or passport ckecksums ## S2100023<6CHE7108019M2002293 # TODO: documentation/help! use strict; my $VERSION=0.1; use Getopt::Long; use Pod::Usage; my $debug = 0; my $verbose = 0; my $full = 0; my ($help, $h); GetOptions( "help" =>\$help, "h|?" => \$h, "debug|d" => \$debug, "verbose|v" => \$verbose, "f|full" => \$full ); pod2usage(-verbose => 2) if $help; pod2usage(-verbose => 1) if ($h); my ($num1, $num2, $num3) = @ARGV; my ($line, $sex, $country); my ($ck1, $ck2, $ck3, $ck_all); # Only do 1 ckecksum of first argument without parsing it, and exit if ($full) { print "'$num1' => ", cksum($num1), "\n"; exit; } # Parse and split arguments if ( $num2 # we got full line2 of ID; split it && ! $num3 && $num2 =~ /(\d{6})\d (.) (\d{6})\d (CHE)?/x ) { # CH ID : 580129 1 M 170312 4 CHE $line = $num2; $num2 = $1; $sex = $2; $num3 = $3; warn "splitted '$line' into '$num2' + '$num3' (sex=$sex)\n" if $debug; } elsif ( ! $num2 && $num1 =~ /([A-Z\d]{8}<) \d ([A-Z0-9]{3}) (\d{6})\d (.) (\d{6})\d/ix ) { # passport : 1-8 9 10 11-13 14-20 21 22-28 29 # S2100023 < 6 CHE 7108019 M 200229 3 $line = $num1; $num1 = $1; $country = $2; $num2 = $3; $sex = $4; $num3 = $5; warn "splitted '$line' into '$num1' + '$num2' + '$num3' (sex=$sex, country=$country)\n" if $debug; } $num1 =~ s/^IDCHE//; # Remove this from start of line1 of ID card # Do separate checksums my $ck1 = cksum($num1); print "$num1 => $ck1\n"; if ($num2) { $ck2 = cksum($num2); print "$num2 => $ck2\n"; } if ($num3) { $ck3 = cksum($num3); print "$num3 => $ck3\n"; } # Do full checksum if (defined($ck1) && defined($ck2) && defined($ck3)) { $ck_all = cksum(join("", $num1, $ck1, $num2, $ck2, $num3, $ck3)); print "Full checksum: $ck_all\n"; } sub cksum { my $num = shift; $num = uc( $num ); # convert tu uppercase $num =~ s/[^A-Z0-9<]//g # and remove spaces etc. && warn "Converted to '$num'\n"; my @digits = split //, $num; my @multipliers = (7,3,1); my $cksum = 0; for (my $i=0; $i < @digits; $i++) { my $n = $digits[$i]; $n = 0 if ($n eq "<"); if ($n =~ /[A-Z]/) { $n = ord( $n ) - 55; # A=>10, B=>11, ..., Z=>35 } $cksum += $n * $multipliers[ $i % 3 ]; } warn "'$num' cksums total = $cksum\n" if $debug; return $cksum % 10; # keep only last digit } __END__ https://www.srf.ch/sendungen/mailbox/was-bedeuten-die-zahlen-auf-der-schweizer-identitaetskarte https://de.wikipedia.org/wiki/Identit%C3%A4tskarte_(Schweiz) https://de.wikipedia.org/wiki/Ausweisnummer https://www.schweizerpass.admin.ch/pass/fr/home/ausweise/daten/maschinenlesbar.html (!! Checksum FAUX!! ) https://icao.int/Security/mrtd/Downloads/Doc%209303/Doc%209303%20French/Doc%209303%20Part%201%20Vol%201.pdf ("Chiffres de contrĂ´le dans la ZLA", page 70) http://home.datacomm.ch/broec/checksum.html (faux!)