views:

1214

answers:

7

I am no way experienced in this type of thing so I am not even sure of the keywords (hence the title). Basically I need a two way function

encrypt(w,x,y) = z

decrypt(z) = w, x, y

Where w = integer x = string (username) y = unix timestamp

and z = is an 8 digit number (possibly including letters, spec isn't there yet.)

I would like z to be not easily guessable and easily verifiable. Speed isn't a huge concern, security isn't either. Tracking one-to-one relationship is the main requirement. Any resources or direction would be appreciated.

EDIT

Thanks for the answers, learning a lot. So to clarify, 8 characters is the only hard requirement, along with the ability to link W <-> Z. The username (Y) and timestamp (Z) are just icing on the camp.

I would like to do this mathematically rather than doing some database looks up, if possible.

If i had to finish this tonight, I could just find a fitting hash algorithm and use a look up table. I am simply trying to expand my understanding of this type of thing and see if I could do it mathematically.

A: 

Hashes by definition are one way only, once hashed, it is very difficult to get the original value back again.

For 2 way encryption i would look at TripleDES which .net has baked right in with TripleDESCryptoServiceProvider.

A fairly straight forward implementation article.

EDIT

It has been mentioned below that you can not cram a lot of information into a small encrypted value. However, for many (not all) situations this is exactly what Bit Masks exist to solve.

Matthew Vines
Hashes aren't a way. The original w, x, and y won't be recoverable after hashing (unless the hash is broken).
erickson
I thought that was what i had said, Hashes, are 1 way, meaning not 2 way, but i can see how you got that, I'll revise.
Matthew Vines
Yes, sorry, I must have misread it as, "hashes are one way to do this." In any case, it's very clear now.
erickson
A: 

I can't tell if you are trying to set this up a way to store passwords, but if you are, you should not use a two way hash function.

If you really want to do what you described, you should just concatenate the string and the timestamp (fill in extra spaces with underscores or something). Take that resulting string, convert it to ASCII or UTF-8 or something, and find its value modulo the largest prime less than 10^8.

twolfe18
No, this is not for passwords.
jskulski
+5  A: 

You just need to encrypt a serialization of (w, x, y) with a private key. Use the same private key to decrypt it.

In any case, the size of z cannot be simply bounded like you did, since it depends on the size of the serialization (since it needs to be two way, there's a bound on the compression you can do, depending on the entropy).

And you are not looking for a hash function, since it would obviously lose some information and you wouldn't be able to reverse it.

EDIT: Since the size of z is a hard limit, you need to restrict the input to 8 bytes, and choose a encryption technique that use 64 bits (or less) block size. Blowfish and Triple DES use 64 bits blocks, but remember that those algorithms didn't receive the same scrutiny as AES.

If you want something really simple and quite unsecure, just xor your input with a secret key.

tonfa
If the size of z is a hard limit (as stated), then w must be small enough to fit into 8 bytes and x and y will have to be forgotten. This info was not available to you at the time you answered, though. +1 for "It isn't a hash that you want".
Jonathan Leffler
@Jonathan I edited the post to reflect it, thanks.
tonfa
+4  A: 
erickson
+1  A: 

Encryption or no encryption, I do not think it is possible to pack that much information into an 8 digit number in such a way that you will ever be able to get it out again.

An integer is 4 bytes. Let's assume your username is limited to 8 characters, and that characters are bytes. Then the timestamp is at least another 4 bytes. That's 16 bytes right there. In hex, that will take 32 digits. Base36 or something will be less, but it's not going to be anywhere near 8.

Licky Lindsay
That makes sense. And if we drop the requirement and only want to use X (a 10 bit unsigned integer - mysql id column)?
jskulski
+2  A: 

You probably can't.

Let's say that w is 32 bits, x supports at least 8 case-insensitive ASCII chars, so at least 37 bits, and y is 32 bits (gets you to 2038, and 31 bits doesn't even get you to now).

So, that's a total of at least 101 bits of data. You're trying to store it in an 8 digit number. It's mathematically impossible to create an invertible function from a larger set to a smaller set, so you'd need to store more than 12.5 bits per "digit".

Of course if you go to more than 8 characters, or if your characters are 16 bit unicode, then you're at least in with a chance.

Steve Jessop
8 characters is a hard requirement I'm afraid. But it really is the only requirement.
jskulski
@jskulski: Given the 8-character hard limit, then you have to hope that w is small enough to be encryptable into 8 bytes; forget x and y because they are not going to fit.
Jonathan Leffler
A: 

Let's formalize your problem, to better study it.

Let k be a key from the set K of possible keys, and (w, x, y) a piece of information, from a set I, that we need to crypt. Let's define the set of "crypted-messages" as A8, where A is the alphabet from which we extract the characters to our crypted message (A = {0, 1, ..., 9, a, b, ..., z, ... }, depending on your specs, as you said).

We define the two functions:

crypt: I * K --> A^8.
decrypt A^8 * K --> I

The problem here is that the size of the set A^8, of crypted-messages, might be smaller than the set of pieces of information (w, x, y). If this is so, it is simply impossible to achieve what you are looking for, unless we try something different...

Let's say that only YOU (or your server, or your application on your server) have to be able to calculate (w, x, y) from z. That is, you might send z to someone, and you don't care that they will not be able to decrypt it.

In this case, what you can do is use a database on your server. You will crypt the information using a well-known algorithm, than you generate a random number z. You define the table:

Id: char[8]
CryptedInformation: byte[]

You will then store z on the Id column, and the crypted information on the corresponding column.

When you need to decrypt the information, someone will give you z, the index of the crypted information, and then you can proceed to decryption.

However, if this works for you, you might not even need to crypt the information, you could have a table:

Id: char[8]
Integer: int
Username: char[]
Timestamp: DateTime

And use the same method, without crypting anything.

This can be applied to an "e-mail verification system" on a subscription process, for example. The link you would send to the user by mail would contain z.

Hope this helps.

Bruno Reis
Thanks for formalizing. Thanks for the information but I am trying to avoid a look up table.
jskulski