Last active
April 11, 2021 17:28
-
-
Save usr-ein/47cf161a84265c75761cb3bcbcd04d50 to your computer and use it in GitHub Desktop.
IBAN Checker and Generator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
""" | |
Checks for the validity of an IBAN number | |
You can either pipe IBANS into it, provide it a file directly, | |
or provide it a single IBAN. In the case of a single IBAN, it'll | |
show you the steps to verify it. | |
Samuel Prevost 2021-03-19 | |
""" | |
import sys | |
from sys import argv | |
def main(): | |
"""Main function""" | |
if len(argv) == 2 and "-h" in argv[1].lower(): | |
print(f"Usage: python {argv[0]} (IBAN | -f iban_list.txt | -f -)") | |
print("Example:") | |
print(f"python {argv[0]} FR76 3000 6000 0112 3456 7890 189") | |
print(f"python {argv[0]} -f iban_list.txt") | |
print(f"cat iban_list.txt | python {argv[0]}") | |
exit(1) | |
if len(argv) == 1 or argv[1] == "-f": | |
iterator = None | |
if len(argv) == 1 or (len(argv) == 3 and argv[2] == "-"): | |
iterator = iter(sys.stdin) | |
else: | |
f = open(argv[2], 'r') | |
iterator = iter(f) | |
all_good = True | |
try: | |
for i, line in enumerate(iterator): | |
line = line.replace("\n", "") | |
if len(line) == 0: | |
continue | |
if not check_iban(line, verbose=False): | |
print(f"Line {i+1}: Found invalid IBAN: {line}", file=sys.stderr) | |
all_good = False | |
if all_good: | |
print("All IBAN were valid", file=sys.stderr) | |
except KeyboardInterrupt: | |
sys.stdout.flush() | |
finally: | |
if hasattr(iterator, "close") and callable(iterator.close): | |
iterator.close() | |
else: | |
iban = ''.join(argv[1:]) | |
if check_iban(iban): | |
print("Valid IBAN") | |
else: | |
print("Invalid IBAN") | |
exit(1) | |
print("Done.", file=sys.stderr) | |
def check_iban(iban, verbose=True): | |
iban = iban.replace(' ', '').upper() | |
if verbose: print("IBAN :\t\t\t", iban) | |
iban = iban[4:] + iban[:4] | |
if verbose: print("IBAN rearranged: \t", iban) | |
iban_int = "" | |
for c in iban: | |
if ord("A") <= ord(c) <= ord("Z"): | |
# A=10, B=11, ..., Z=35 that's why letters count double, | |
# because they're encoded on two decimal digits | |
iban_int += str(ord(c)-55) | |
else: | |
iban_int += c | |
if verbose: print("IBAN in int: \t\t", iban_int) | |
iban_int = int(iban_int) | |
if verbose: print("Mod 97: \t\t", iban_int % 97) | |
return iban_int % 97 == 1 | |
if __name__ == "__main__": | |
main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
""" | |
Generates valid IBAN numbers that (possibly) don't exist | |
For now it only works with French (FR) IBAN | |
To add your country, just find out the length of your | |
BBAN by counting the number of digits after the first | |
four chars. Letters in that BBAN part count for two chars. | |
Samuel Prevost 2021-03-19 | |
""" | |
from sys import argv | |
import numpy as np | |
def main(): | |
"""Main function""" | |
if len(argv) != 3: | |
print(f"Usage: python3 {argv[0]} COUNTRY IBAN_COUNT") | |
exit(1) | |
# IBAN: | |
# FR896537193277159353833949 | |
# ^^^^^^^^^^^^^^^^^^^^^^ | |
# Basic Bank Acc Num (BBAN) | |
bban_sizes = { | |
"FR": 23 | |
} | |
country = argv[1] | |
if country not in bban_sizes.keys(): | |
print("Country not yet supported !") | |
print("Possible countries:") | |
print(", ".join(bban_sizes.keys())) | |
exit(1) | |
country_code = get_country_code(country) | |
for _ in range(int(argv[2])): | |
while True: | |
try: | |
r = list(np.random.randint(0, 10, size=bban_sizes[country])) | |
r += [country_code] | |
r = int("".join(str(n) for n in r)) | |
check_digits = bruteforce_check_digit(r) | |
iban_int = add_num(r, check_digits) | |
iban = to_string_iban(iban_int) | |
print(iban) | |
break | |
except CheckDigitsError: | |
continue | |
class CheckDigitsError(ValueError): | |
pass | |
def bruteforce_check_digit(r): | |
for i in range(10, 100): | |
iban_int = add_num(r, i) | |
if iban_int % 97 == 1: | |
return i | |
raise CheckDigitsError("Didn't find suitable check digit !") | |
def add_num(a,b): | |
return int(str(a) + str(b)) | |
def get_country_code(country_letters): | |
# FR -> 1527 | |
return int("".join([str(ord(c)-55) for c in country_letters])) | |
def to_string_iban(int_iban): | |
str_iban = str(int_iban) | |
str_iban = str_iban[-6:] + str_iban[:-6] | |
str_iban = chr(int(str_iban[:2])+55) + chr(int(str_iban[2:4])+55) + str_iban[4:] | |
return str_iban | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment