Reputation: 83
I've run across some PIN encoding which I'm trying to figure out so I can improve upon a web application used at my place of work.
When I reset users' PINs (in this case, just my own for testing purposes), I'm seeing the following:
PIN VALUE
000000 = 7F55858585858585
111111 = 7F55868686868686
222222 = 7F55878787878787
999999 = 7F558E8E8E8E8E8E
000001 = 7F01313131313132
000011 = 7F55858585858686
000111 = 7F01313131323232
001111 = 7F55858586868686
011111 = 7F01313232323232
000002 = 7F02323232323234
100000 = 7F01323131313131
111112 = 7F03343434343435
123456 = 7F0738393A3B3C3D
654321 = 7F073D3C3B3A3938
1357924680 = 7F01323436383A3335373931
1111111111 = 7F5586868686868686868686
1234567890 = 7F0132333435363738393A31
It's clearly just hex, and always starts with 7F (1111111 or 127), but I'm not seeing a pattern for how the next two characters are chosen. Those two characters seem to be the determining value for converting the PIN.
For example:
000000 = 7F 55 858585858585
7F (hex) = 127 (dec) or 1111111 (bin) ## appears to not be used in the calculation?
55 (hex) = 85 (dec) or 1010101 (bin)
0 (PIN) + 85 = 85
000000 = 858585858585
111111 = 7F 55 868686868686
7F (hex) = 127 (dec) or 1111111 (bin) ## appears to not be used in the calculation?
55 (hex) = 85 (dec)
1 (PIN) + 85 = 86
111111 = 868686868686
But then also:
1357924680 = 7F 01 323436383A3335373931
01 (hex) = 31 (dec) ?
1 (PIN) + 31 = 32
1357924680 = 323436383A3335373931
Any help pointing me in the right direction would be greatly appreciated.
Upvotes: 0
Views: 77
Reputation: 30103
I don't see enough data in your minimal reproducible example to uncover an algorithm how the pinshift
value should be determined (supplied to the pin_to_hex
function). A random value is used in the following solution:
def hex_to_pin( pinhex: str) -> list:
'''
decode a PIN from a particular hexadecimal-formatted string
hex_to_pin('7F0738393A3B3C3D')
inverse of the "pin_to_hex" function (any of the following):
hex_to_pin(pin_to_hex('123456', 7))
pin_to_hex(*hex_to_pin('7F0738393A3B3C3D'))
'''
xxaux = bytes.fromhex(pinhex)
return [bytes([x - xxaux[1] for x in xxaux[2:]]).decode(),
xxaux[1]]
def pin_to_hex( pindec: str, pinshift: int, upper=False) -> str:
'''
encode a PIN to a particular hexadecimal-formatted string
pin_to_hex('123456', 7)
inverse of the "hex_to_pin" function (any of the following):
pin_to_hex(*hex_to_pin('7F0738393A3B3C3D'),True)
hex_to_pin(pin_to_hex('123456', 7))
'''
shift_ = max( 1, pinshift % 199) ## 134 for alpha-numeric PIN code
retaux = [b'\x7F', shift_.to_bytes(1, byteorder='big')]
for digit_ in pindec.encode():
retaux.append( (digit_ + shift_).to_bytes(1, byteorder='big'))
if upper:
return (b''.join(retaux)).hex().upper()
else:
return (b''.join(retaux)).hex()
def get_pin_shift( pindec: str) -> int:
'''
determine "pinshift" parameter for the "pin_to_hex" function
currently returns a random number
'''
return random.randint(1,198) ## (1,133) for alpha-numeric PIN code
hexes = [
'7F01323436383A3335373931',
'7F0738393A3B3C3D',
'7F558E8E8E8E8E8E'
]
print("hex_to_pin:")
maxlen = len( max(hexes, key=len))
deces = []
for xshex in hexes:
xsdec = hex_to_pin( xshex)
print( f"{xshex:<{maxlen}} ({xsdec[1]:>3}) {xsdec[0]}")
deces.append(xsdec[0])
import random
print("pin_to_hex:")
for xsdec in deces:
xsshift = get_pin_shift( xsdec)
xshex = pin_to_hex( xsdec, xsshift)
print( f"{xshex:<{maxlen}} ({xsshift:>3}) {xsdec}")
Output SO\71875753.py
hex_to_pin:
7F01323436383A3335373931 ( 1) 1357924680
7F0738393A3B3C3D ( 7) 123456
7F558E8E8E8E8E8E ( 85) 999999
pin_to_hex:
7f1041434547494244464840 ( 16) 1357924680
7f4e7f8081828384 ( 78) 123456
7f013a3a3a3a3a3a ( 1) 999999
Upvotes: 1