15

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) would be considered icing on the cake.

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.

dmcgill50
  • 528
  • 6
  • 26
jon skulski
  • 2,305
  • 3
  • 18
  • 27
  • Could you clarify why you need to be able to decrypt z into those values ? – Zed Aug 25 '09 at 20:08
  • 2
    A comment, not an answer: you do not avoid the key sharing problem in this scenario, just to make sure you have thought of this. – San Jacinto Aug 25 '09 at 20:09
  • @Zed, we want to decrypt to avoid a possibly long look up on an external table. However, it's a soft requirement, but a nice one. – jon skulski Aug 25 '09 at 22:28

7 Answers7

23

Encryption vs. Hashing

This is an encryption problem, since the original information needs to be recovered. The quality of a cryptographic hash is judged by how difficult it is to reverse the hash and recover the original information, so hashing is not applicable here.

To perform encryption, some key material is needed. There are many encryption algorithms, but they fall into two main groups: symmetric and asymmetric.

Application

The application here isn't clear. But if you are "encrypting" some information and sending it somewhere, then later getting it back and doing something with it, symmetric encryption is the way to go. For example, say you want to encode a user name, an IP address, and some identifier from your application in a parameter that you include in a link in some HTML. When the user clicks the link, that parameter is passed back to your application and you decode it to recover the original information. That's a great fit for symmetric encryption, because the sender and the recipient are the same party, and key exchange is a no-op.

Background

In symmetric encryption, the sender and recipient need to know the same key, but keep it secret from everyone else. As a simple example, two people could meet in person, and decide on a password. Later on, they could use that password to keep their email to each other private. However, anyone who overhears the password exchange will be able to spy on them; the exchange has to happen over a secure channel... but if you had a secure channel to begin with, you wouldn't need to exchange a new password.

In asymmetric encryption, each party creates a pair of keys. One is public, and can be freely distributed to anyone who wants to send a private message. The other is private. Only the message recipient knows that private key.

A big advantage to symmetric encryption is that it is fast. All well-designed protocols use a symmetric algorithm to encrypt large amounts of data. The downside is that it can be difficult to exchange keys securely—what if you can't "meet up" (virtually or physically) in a secure place to agree on a password?

Since public keys can be freely shared, two people can exchange a private message over an insecure channel without having previously agreed on a key. However, asymmetric encryption is much slower, so its usually used to encrypt a symmetric key or perform "key agreement" for a symmetric cipher. SSL and most cryptographic protocols go through a handshake where asymmetric encryption is used to set up a symmetric key, which is used to protect the rest of the conversation.

erickson
  • 265,237
  • 58
  • 395
  • 493
11

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
  • 24,151
  • 2
  • 35
  • 41
  • 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 Aug 25 '09 at 22:46
4

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
  • 273,490
  • 39
  • 460
  • 699
  • 8 characters is a hard requirement I'm afraid. But it really is the only requirement. – jon skulski Aug 25 '09 at 22:33
  • @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 Aug 25 '09 at 22:48
  • > It's mathematically impossible to create an invertible function from a larger set to a smaller set: isn't that what compressing does? – gotofritz Feb 05 '15 at 18:29
  • 1
    @gotofritz: no, it doesn't. Consider for example the set of all possible 10k files, there are 2^10240 of them. Your compression algorithm maps these to exactly the same number (2^10240) of different compressed files. Some of those are smaller than 10k, some are larger. – Steve Jessop Feb 05 '15 at 22:38
2

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
  • 37,201
  • 11
  • 119
  • 156
1

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
  • 27,253
  • 7
  • 76
  • 97
  • Hashes aren't a way. The original w, x, and y won't be recoverable after hashing (unless the hash is broken). – erickson Aug 25 '09 at 20:11
  • 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 Aug 25 '09 at 20:13
  • Yes, sorry, I must have misread it as, "hashes are one way to do this." In any case, it's very clear now. – erickson Aug 25 '09 at 20:35
1

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
  • 2,228
  • 4
  • 24
  • 25
1

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
  • 1,048
  • 6
  • 10
  • That makes sense. And if we drop the requirement and only want to use X (a 10 bit unsigned integer - mysql id column)? – jon skulski Aug 25 '09 at 22:38