views:

751

answers:

4

Client has an simple increasing order number (1, 2, 3...). He wants end-users to receive an 8- or 9- digit (digits only -- no characters) "random" number. Obviously, this "random" number actually has to be unique and reversible (it's really an encryption of the actualOrderNumber).

My first thought was to just shuffle some bits. When I showed the client a sample sequence, he complained that subsequent obfuscOrderNumbers were increasing until they hit a "shuffle" point (point where the lower-order bits came into play). He wants the obfuscOrderNumbers to be as random-seeming as possible.

My next thought was to deterministically seed a linear congruential pseudo-random-number generator and then take the actualOrderNumber th value. But in that case, I need to worry about collisions -- the client wants an algorithm that is guaranteed not to collide in at least 10^7 cycles.

My third thought was "eh, just encrypt the darn thing," but if I use a stock encryption library, I'd have to post-process it to get the 8-or-9 digits only requirement.

My fourth thought was to interpret the bits of actualOrderNumber as a Gray-coded integer and return that.

My fifth though was: "I am probably overthinking this. I bet someone on StackOverflow can do this in a couple lines of code."

+2  A: 

Will the client require the distribution of obfuscated consecutive order numbers to look like anything in particular?

If you do not want to complicate yourself with encryption, use a combination of bit shuffling with a bit of random salting (if you have bits/digits to spare) XOR-superimposed over some fixed constant (or some function of something that would be readily available alongside the obfuscated order ID at any time, such as perhaps the customer_id who placed the order?)


EDIT

It appears that all the client desires is for an outside party to not be able to infer the progress of sales. In this case a shuffling solution (bit-mapping, e.g. original bit 1 maps to obfuscated bit 6, original bit 6 maps to obfuscated bit 3, etc.) should be more than sufficient. Add some random bits if you really want to make it harder to crack, provided that you have the additional bits available (e.g. assuming original order numbers go only up to 6 digits, but you're allowed 8-9 in the obfuscated order number, then you can use 2-3 digits for randomness before performing bit-mapping). Possibly XOR the result for additional intimidation (an inquisitive party might attempt to generate two consecutive obfuscated orders, XOR them against each other to get rid of the XOR constant, and would then have to deduce which of the non-zero bits come from the salt, and which ones came from an increment, and whether he really got two consecutive order numbers or not... He would have to repeat this for a significant number of what he'd hope are consecutive order numbers in order to crack it.)


EDIT2

You can, of course, allocate completely random numbers for the obfuscated order IDs, store the correspondence to persistent storage (e.g. DB) and perform collision detection as well as de-obfuscation against same storage. A bit of overkill if you ask me, but on the plus side it's the best as far as obfuscation goes (and you implement whichever distribution function your soul desires, and you can change the distribution function anytime you like.)

Cheers, V.

vladr
Re Edit2: Yes, I thought of that, and I like the determinism of it (change the function at any time and still be able to recover legacy obfuscation). But it just seems too damn ugly.
Larry OBrien
AWWW! You guys who deleted your answers removed some valuable ideas about what does and doesn't work. All discussion is valuable!
MrChrister
+4  A: 

Hash function? http://www.partow.net/programming/hashfunctions/index.html

Brian Knoblauch
Hash function is not reversible!!
vladr
If you're using it as an order number, why would you want to reverse it? I can't think of a reason you would want to know "this is your nth order", and there are probably better ways of doing it anyway.
job
Internally, they need to use the original order number. Just when they publish the order number outside the system, or read a number that they've previously published, they need to convert.
erickson
What's so broken about their system that they can't store the hash alongside the original number internally? This is a *good* answer!
Ant P.
The system is not "broken," (and least not on this issue! :-) ) it's non-trivial. There are several applications interacting with the database. Obscurely "formatting" the number is (I think) preferable to manipulating the persistence layer, with fewer consequences. YMMV.
Larry OBrien
+2  A: 

In 9 digit number, the first digit is a random index between 0 and 7 (or 1-8). Put another random digit at that position. The rest is the "real order number:

  • Orig order: 100
  • Random index: 5
  • Random digit: 4 (guaranteed, rolled a dice :) )
  • Result: 500040100

  • Orig Nr: 101

  • Random index: 2
  • Random digit 6
  • Result: 200001061

You can decide that the 5th (or any other) digit is the index.

Or, if you can live with real order numbers of 6 digits, then you can introduce "secondary" index as well. And you can reverse the order of the digits in the "real" order nr.

Sunny
+1 for the random digit “4” being obtained by the roll of a dice. :)
Bombe
+3  A: 

Pick a 8 or 9 digit number at random, say 839712541. Then, take your order number's binary representation (for this example, I'm not using 2's complement), pad it out to the same number of bits (30), reverse it, and xor the flipped order number and the magic number. For example:

1         = 000000000000000000000000000001

Flip      = 100000000000000000000000000000
839712541 = 110010000011001111111100011101
XOR       = 010010000011001111111100011101 = 302841629

2         = 000000000000000000000000000010

Flip      = 010000000000000000000000000000
839712541 = 110010000011001111111100011101
XOR       = 100010000011001111111100011101 = 571277085

To get the order numbers back, xor the output number with your magic number, convert to a bit string, and reverse.

Pesto
What are you talking about? All palindromes have a unique binary representation, and thus will result in a unique xor'd value.
Pesto
@Vlad: I think I've figured out what you're saying. You think the algorithm is order_num XOR flipped_order_num XOR magic_num, but it's not. It's flipped_order_num XOR magic_num. The flipping is just so that incrementing an order number by 1 has a large effect on the outputted number.
Pesto
Oh ok, that is fine actually. :) Sorry, removing my previous comment.
vladr