This is an easy problem. It's been solved quite simply :-)
It's used to encrypt hard-drives: you don't want to change the hard-drive encryption key every time a user wants to change their password.
I can write you 3 simple programs by the end of the day that:
- the first accepts a pin and key, returning the intermediate
- the second accepts a pin and intermediate, returning the key
- the third accepts a pin, an intermediate and a new pin, returning the new intermediate (that will produce the same key)
(well, probably one program that does all 3 things)
Cryptologically, I can guarantee that:
- Using only the pin, one can't recreate the key or the intermediate
- Using only the key, one can't recreate the pin or the intermediate
- Using only the intermediate, one can't recreate the pin or the key
- Using both key and intermediate, one can't recreate the pin
In addition, there is an improvement I can make to your design: adding salt. Salt is a NON-SECRET field that is UNIQUE per-user, in addition to the secret pin. Examples of salt are the user-name, or the user index from the database. It doesn't add cryptological strength when dealing with an attack on a single user, BUT if someone gets the database of all users and is trying to "just attack one, no matter which one", then salt makes the attack much much harder.